반응형

In the first article of this series, I discussed how to prevent users from printing directly from Flash’s default context menu. In the second article, I wrote about using the AlivePDF library to create PDFs from your application for printing or saving. Today I will be covering how to print application data from Flex.

The Flex framework comes with several classes to assist you in printing; however, they don’t tend to produce the greatest results. The PrintDataGrid class is probably your best option when it comes to printing data, but whenever I’ve worked with it in the past I’ve always ended up disappointed. Even after formatting, there are always issues; like rows getting cut off and pages breaks in weird places.

There have been talks about AlivePDF offering a Grid class to the library, which may be interesting, but it hasn’t been released and would probably only handle DataGrid data. So the best option I’ve found for printing data is to allow a server to generate a PDF, which can then be sent back to Flex and output however you want.

A great benefit of this option is the ability to easily format the print results. You can use simple HTML and CSS to customize the printout much easier than you could using ActionScript. You can also add custom styles, letterheads, and footers to fit your needs.

Here is an example, you can view the source here.

This will work with any server-side script that can generate PDF content. In this demo I am using a ColdFusion script which simply generates an HTML table and returns it in PDF format. You can view the ColdFusion code along with the rest of the source code here.

The data from the server is returned to Flex as a ByteArray. You can take that ByteArray and output it to the user in several different ways:

Using the Browser

Similar to the last article in this series, you can send the results to a server-side script to either display the PDF in the browser or prompt the user to open or save the document. This time, however, the AlivePDF library is no longer necessary since the PDF binary has already been created.

private function generatePDF(pdfBinary:ByteArray, method:String):void{

 

//result comes back as binary, create a new URL request and pass it back to the server
var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
var urlString:String = "http://kalengibbons.com/assets/pages/pdfCreator.cfm";
if(method == "inline")

 

urlString += "?method=inline";

else

urlString += "?method=attachment&name=dataPrintSample.pdf";

var sendRequest:URLRequest = new URLRequest(urlString);
sendRequest.requestHeaders.push(header);
sendRequest.method = URLRequestMethod.POST;
sendRequest.data = pdfBinary;
navigateToURL(sendRequest, "_blank");

}

Save locally from Flash

Thanks to new capabilities in Flash Player 10, you can also provide users the option to save the PDF directly from Flash. This eliminates the need for the second server-side script.

private function savePDF(pdfBinary:ByteArray):void{

 

var fileRef:FileReference = new FileReference();
fileRef.save(pdfBinary, "yourPrintout.pdf");

}

If you plan to use the save functionality you’ll need to set the “Required Flash Player version” in your project preferences to 10.0.0 or higher or else Flex Builder will complain. You have to remember that only users with Flash Player 10 or above will be able to use this functionality, so some type of version validation is a necessity.

Another caveat is that the save method must be triggered by some type of user interaction, that’s why the sample application prompts for system access before saving. Although the user has clicked the “Print Data” button, that event is lost when the server call is made, so we need another interaction from the user.

 

Posted by 1010
반응형
Posted by 1010
반응형

Using the FlexPrintJob class

You use the FlexPrintJob class to print one or more Flex objects, such as a Form or VBox container. For each object that you specify, Flex prints the object and all objects that it contains. The objects can be all or part of the displayed interface, or they can be components that format data specifically for printing. The FlexPrintJob class lets you scale the output to fit the page, and automatically uses multiple pages to print an object that does not fit on a single page.

You use the FlexPrintJob class to print a dynamically rendered document that you format specifically for printing. This capability is especially useful for rendering and printing such information as receipts, itineraries, and other displays that contain external dynamic content, such as database content and dynamic text.

You often use the FlexPrintJob class within an event listener. For example, you can use a Button control with an event listener that prints some or all of the application.

Note: The FlexPrintJob class causes the operating system to display a Print dialog box. You cannot print without some user action.

 

Build and send a print job

You print output by building and sending a print job.

  1. Create an instance of the FlexPrintJob class:
    var printJob:FlexPrintJob = new FlexPrintJob();
    
    
  2. Start the print job:
    printJob.start(); 
    
    

    This causes the operating system to display a Print dialog box.

  3. Add one or more objects to the print job and specify how to scale them:
    printJob.addObject(myObject, FlexPrintJobScaleType.MATCH_WIDTH); 
    
    

    Each object starts on a new page.

  4. Send the print job to the printer:
    printJob.send(); 
    
    
  5. Free up any unneeded objects.

Note: Because you are spooling a print job to the user's operating system between your calls to the start() and send() methods, you should limit the code between these calls to print-specific activities. For example, the content should not interact with the user between the start() and send() methods.

 

The following sections detail the procedures to use in these steps.

Starting a print job

To start a print job, you create an instance of the FlexPrintJob class and call its start() method. This method prompts Flash Player or Adobe® AIR™ to spool the print job to the user's operating system, which causes the user's operating system to display a Print dialog box.

If the user selects an option to begin printing from the Print dialog box, the start() method returns a value of true. If the user cancels the print job, the return value is false. After the user exits the operating system Print dialog box, the start() method uses the printer information to set values for the FlexPrintJob object's pageHeight and pageWidth properties, which represent the dimensions of the printed page area.

Note: Depending on the user's operating system, an additional dialog box might appear until spooling is complete and the application calls the send() method.

 

Only one print job can be active at a time. You cannot start a second print job until one of the following has happened with the previous print job:

  • The start() method returns a value of false (the job failed).
  • The send() method completes execution following a successful call to the addObject() method. Because the send() method is synchronous, code that follows it can assume that the call completed successfully.

Adding objects to the print job

You use the addObject() method of the FlexPrintJob class to add objects to the print job. Each object starts on a new page; therefore, the following code prints a DataGrid control and a Button control on separate pages:

printJob.addObject(myDataGrid);
printJob.addObject(myButton);

Scaling a print job

The scaleType parameter of the addObject() method determines how to scale the output. Use the following FlexPrintJobScaleType class constants to specify the scaling method:

Constant

Action

MATCH_WIDTH

(Default) Scales the object to fill the available page width. If the resulting object height exceeds the page height, the output spans multiple pages.

MATCH_HEIGHT

Scales the object to fill the available page height. If the resulting object width exceeds the page width, the output spans multiple pages.

SHOW_ALL

Scales the object to fit on a single page, filling one dimension; that is, it selects the smaller of the MATCH_WIDTH or MATCH_HEIGHT scale types.

FILL_PAGE

Scales the object to fill at least one page completely; that is, it selects the larger of the MATCH_WIDTH or MATCH_HEIGHT scale types.

NONE

Does not scale the output. The printed page has the same dimensions as the object on the screen. If the object height, width, or both dimensions exceed the page width or height, the output spans multiple pages.

If an object requires multiple pages, the output splits at the page boundaries. This can result in unreadable text or inappropriately split graphics. For information on how to format your print job to avoid these problems, see Printing multipage output.

The FlexPrintJob class includes two properties that can help your application determine how to scale the print job. These properties are read-only and are initially 0. When the application calls the start() method and the user selects the Print option in the operating system Print dialog box, Flash Player or AIR retrieves the print settings from the operating system. The start() method populates the following properties:

Property

Type

Unit

Description

pageHeight

Number

Points

Height of the printable area on the page; does not include any user-set margins.

pageWidth

Number

Points

Width of the printable area on the page; does not include any user-set margins.

Note: A point is a print unit of measurement that is 1/72 of an inch. Flex automatically maps 72 pixels to one inch (72 points) of printed output, based on the printer settings.

 

Completing the print operation

To send the print job to a printer after using the FlexPrintJob addObject() method, use the send() method, which causes Flash Player or AIR to stop spooling the print job so that the printer starts printing.

After sending the print job to a printer, if you use print-only components to format output for printing, call removeChild() to remove the print-specific component. For more information, see Using a print-specific output format.

Example: A simple print job

The following example prints a DataGrid object exactly as it appears on the screen, without scaling:

<?xml version="1.0"?>
<!-- printing\DGPrint.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

    <mx:Script>
        <![CDATA[
            import mx.printing.*;

            // Create a PrintJob instance.
            private function doPrint():void {
                // Create an instance of the FlexPrintJob class.
                var printJob:FlexPrintJob = new FlexPrintJob();

                // Start the print job.
                if (printJob.start() != true) return;

                // Add the object to print. Do not scale it.
                printJob.addObject(myDataGrid, FlexPrintJobScaleType.NONE);

                // Send the job to the printer.
                printJob.send();
            }
        ]]>
    </mx:Script>

    <mx:VBox id="myVBox">
        <mx:DataGrid id="myDataGrid" width="300">
            <mx:dataProvider>
                <mx:Object Product="Flash" Code="1000"/>
                <mx:Object Product="Flex" Code="2000"/>
                <mx:Object Product="ColdFusion" Code="3000"/>
                <mx:Object Product="JRun" Code="4000"/>
            </mx:dataProvider>
        </mx:DataGrid>
        <mx:Button id="myButton" 
            label="Print" 
            click="doPrint();"/>
    </mx:VBox>
</mx:Application>

The executing SWF file for the previous example is shown below:

 

In this example, selecting the Button control invokes the doPrint() event listener. The event listener creates an instance of the FlexPrintJob class to print the DataGrid control, adds the DataGrid control to the print job using the addObject() method, and then uses the send() method to print the page.

To print the DataGrid and Button controls on your page, specify myVBox, the ID of the object that contains both controls, in the addObject() method's object parameter. If want to print the DataGrid and Button controls on separate pages, specify each object in a separate addObject() method.

To print the DataGrid control so that it spans the page width, omit the second addObject() method parameter, or specify FlexPrintJobScaleType.MATCH_WIDTH. To print the DataGrid control with the largest size that fits on a single page, specify FlexPrintJobScaleType.SHOW_ALL. In the previous example, FlexPrintJobScaleType.SHOW_ALL has the same result as FlexPrintJobScaleType.MATCH_WIDTH because the DataGrid is short.

Posted by 1010
반응형

Flex printing breakthrough! And more!

Hi guys. It’s occurred to me that this blog is wandering off topic. It’s not cheap pr0n, nor is it really supposed to be about feelings and girly rubbish… but sober, hard core technical discussions. Yes, I know cinnkitty finds those a turn on, but she also likes footy and beer.

Today is all about printing in Flex and AIR. While there is a fair amount of information out there about printing in Flex/AIR, the fact remains that at the end of the day, it’s not really designed for printing. The output is, I’ve found, unreliable and  fuzzy, essentially an image of the print area (sometimes with the scroll bars showing!), rather than a true print.

An example is this:

// AS3 code:

public textArea1:TextArea = new TextArea();

public function printText(e:Event = null):void{
trace(‘printing Plain Text’)
textArea1.htmlText = stringToPrint;//this is where the string to print goes                                                                                     textArea1.width = 800;
textArea1.height = 1000;
textArea1.visible = false;
var timer : Timer = new Timer( 1000, 1 );
timer.addEventListener(TimerEvent.TIMER, function():void{
try{
var PrintJob:FlexPrintJob = new FlexPrintJob;
PrintJob.start()
PrintJob.printAsBitmap=false;
PrintJob.addObject(Browser, FlexPrintJobScaleType.SHOW_ALL)
PrintJob.send()
PrintJob = null
Browser.height = 0;
Browser.width = 0;
}catch(e:Error){
//
}
} );
timer.start();
}

//end AS3 code

which  is fine for simple text, up to a point can even parse html a bit. Images? think again… More than a few pages? You’re joking, right?

The answer is actually on it’s way, in the form of http://www.alivepdf.org/

This guy has figured out a way of  constructing a PDF from scratch, and if you’re tricky, you can use AIR’s new PDF capabilities (Warning! You will need the latest version of Acrobat, and only the latest version of Acrobat will work! Too bad if, like me you prefer something lighter, like foxit) to construct what you want to print, save it to a temp file, then bring it up in a PDF control in AIR.

The PDF  control, being essentially Acrobat, takes care of printing, previewing, and even saving a copy to disk if your target audience don’t want to print (wasting paper and all).

A brief introduction to PDF in AIR can be found at http://agknology.com/blog/http:/agknology.com/blog/air/29 

But wait, there’s more!

If you think about it, you can do lots of things in PDF. Now that we can generate them dynamically (sort of, but there’s promises of more to come) some implications  are:

- Freedom from FLV as teh only video format (PDFs can handle WMV)

- E-book applications

- lightweight, customised PDF deployment (as the PDF’s can be generated procedurally using a few lines of (compressible) text/code, rather than the actual PDF itself being stored)

A note on the last thing- Thibault Imbert, the author, has also had a crack at a Zip class for AS3. Interesting…

A sample PDF generation application is as follows:

<?xml version=”1.0″ encoding=”utf-8″?>
< mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml&#8221; layout=”absolute” applicationComplete=”init()”>
< mx:Script>
< ![CDATA[
import org.alivepdf.fonts.Style;
import org.alivepdf.layout.Layout;
import org.alivepdf.pdf.PDF;
import org.alivepdf.*
import org.alivepdf.saving.Method;
import mx.controls.Alert;
import mx.events.CloseEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;

private var display:HTMLControl;
private    var someText:String = 'Hello World';
private var actualPDF:ByteArray = new ByteArray();
private var pdf:PDF = new PDF();
//    private var page1:org.alivepdf.
private function init():void{

pdf.addPage();
}

private function showClicked(e:Event = null):void{

pdf.setFont('Arial','',10);

pdf.writeText(5, someText);
var size:int = 20;
pdf.setFontSize(size);
pdf.writeText(size, someText);
size= 5;
pdf.setFontSize(size);
pdf.writeText(size, someText);
//    pdf.writeText(size, pdf.setFontSize(10)'Hello');;
//pdf.addMultiCell(100,50,someText,1);
trace('about to save pdf');
actualPDF = pdf.savePDF(Method.LOCAL);
//trace('text size is'+pdf
//pdf.addEventListener(Event.COMPLETE, saveIt);
}

private function saveIt(e:Event = null):void{
var file:File = File.desktopDirectory;
file = file.resolvePath('temp.pdf' );
var stream:FileStream = new FileStream();;
stream.open(file, FileMode.WRITE);
stream.writeBytes( actualPDF);
stream.close();
trace('Saved file');
}

public var stringToDisplay:String = new String();

private var desktopDir:File = File.desktopDirectory;
public function openString(e:Event = null):void{

try {
desktopDir.browseForOpen('Pick a file');
desktopDir.addEventListener(Event.SELECT, openFile);
}catch(error:Error){
Alert.show('There was an error writing a file', "Error", Alert.OK )
}
}

private function openFile(e:Event = null):void{
//trace('Storage/openFile is opening file:'+path)

var file:File = e.target as File;
if (file.exists){
var stream:FileStream = new FileStream()
stream.open(file, FileMode.READ);
someText = stream.readUTFBytes(stream.bytesAvailable);
stream.close()
}else{
Alert.show("There was an error reading a file", "Error");

}

}

]]>

</mx:Script>
< mx:Button x=”111″ y=”105″ label=”make pdf” click=”showClicked()”/>
< mx:Button x=”111″ y=”151″ label=”save it” click=”saveIt();”/>
< mx:Button x=”110″ y=”57″ label=”open text” click=”openString()”/>
< /mx:WindowedApplication>

have fun! and I’ll get back to matters of the flesh, soon enough…

Posted by 1010
반응형

AlivePDF

ActionScript 3 Open-Source PDF Library – 100% client side PDF generation which makes you and your server happy ;)

AlivePDF 0.1.5 [RC] new API’s

AlivePDF 0.1.5 RC is finally there and has a lot of new features. As you may have seen, font embedding is now supported and a lot of new things, as a result a lot of new API's have been introduced. Hopefully the documentation is now updated. To get started with those API's here is a brief introduction :

Font Embedding support :

You can embed TTF or PFM fonts now and use them through your PDF document. To use an embedded font, just use the EmbeddedFont class and pass the TTF stream to the EmbeddedFont class constructor, then pass it to the setFont method which will automatically call the addFont method if needed :

var msg:String = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."

 

var myEmbeddedFont:EmbeddedFont = new EmbeddedFont( new fontStream(), new afmStream(), CodePage.CP1252 );
myPDF.addPage();
myPDF.setFont( myEmbeddedFont, 20 );
myPDF.writeText(12, msg);

As you can see, three parameters are required, the first one is the font stream as a ByteArray, the second one is the AFM file which describes the font, that you can get easily for a font or generate it through a tool like TTF2PT1 or even online here. The third parameter is the codepage to use for your font, which defines the character code mapping between 0 and 255. Note that, only CodePage.1252 is supported for now, but this will be fixed in the final release.

If you want to use standard fonts, known as non-embedded fonts, just use the CoreFont class :

var msg:String = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."

 

var myCoreFont:IFont = new CoreFont ( FontFamily.HELVETICA_BOLD );
myPDF.addPage();
myPDF.setFont( myCoreFont, 20 );
myPDF.writeText(12, msg);

Cell API additions

New Cell API's have been introduced allowing you to force its content to fit in a cell :

var shortText:String = 'This text is short enough.';
var longText:String = 'This text is way too long.';

 

p.addCellFitScale(0, 10,shortText,1,1);
p.addCellFitScale(0, 10,longText,1,1,'',1);

p.addCellFitScaleForce(0,10,shortText,1,1,'',1);
p.addCellFitScaleForce(0,10,longText,1,1,'',1);

p.addCellFitSpace(80,10,shortText,1,1);
p.addCellFitSpace(80,10,longText,1,1,'',1);

p.addCellFitSpaceForce(0,10,shortText,1,1,'',1);
p.addCellFitSpaceForce(0,10,longText,1,1,'',1);

EPS Support :

You can now draw EPS or AI files (8.0 version or below required) into your PDF, by using the addEPSImage method, more infos here : http://alivepdf.bytearray.org/?p=358

A drawSector() API has been added to allow you to create dynamic charts very easily, the following code creates a simple pie chart :

var xCenter:int = 105;
var yCenter:int = 60;
var radius:int = 40;

 

p.lineStyle( new RGBColor ( 0x000000 ), .1 );
p.beginFill( new RGBColor ( 0x0099CC ) );
p.drawSector(xCenter, yCenter, radius, 20, 120);
p.beginFill( new RGBColor ( 0x336699 ) );
p.drawSector(xCenter, yCenter, radius, 120, 250);
p.beginFill( new RGBColor ( 0x6598FF ) );
p.drawSector(xCenter, yCenter, radius, 250, 20);

BeginBitmapFill Drawing API

Just like in Flash Player, a beginBitmapFill API has been added, more infos here : http://alivepdf.bytearray.org/?p=324

Spot Color Support

As requested, Spot color support has been introduced, more infos here : http://alivepdf.bytearray.org/?p=296

Document-level navigation

As requested, document-level navigation has been added, more infos here : http://alivepdf.bytearray.org/?p=279

CSV Grid Export

As requested, the Grid class allows you to export its data as a CSV file: http://alivepdf.bytearray.org/?p=259

So, what's next ? Some final fixes in the UnicodePDF class for other characters like (greek, russian) and a new Invoice API to make invoice generation smooth. I have to finalize the integration of the AlivePDF extension for templates created by Gonzalo. But I may have some interesting news concerning exporting drawn vectors to the PDF, without having to rasterize stuff ;)

Posted by 1010
반응형

출처 : http://flystone.tistory.com/61

SpriteVisualElement (Spark) | Flex Examples 

SpriteVisualElement 는 Flex 의 UIComponent 같은 역활을 한다.
Flex 에 Sprite 객체를 넣을 수 있는 Element 이다.

클래스명에서 보는것과 같이 Elemet 이므로 다른 컴포넌트에 넣을때에는
addChild() 가 아닌 addElement() 로  해야만 정상 작동 한다.

또한 이 엘리멘트를 사용할 때 주의해야 할 것은
아무리 addChild() 로 Sprite 객체든 다른 것들을 넣고 transform 작업을 하여도
실질적으로 이 엘리멘트에 width, height 값은 변하지 않는다.

따라서 직접적으로 width, height 를 변환해 주어야 다른 컴포넌트들과의 위치 정렬및 스크롤러 생성 등의
좌표 관련 설정이 원하는데로 나올 것 이다. 

 

Posted by 1010
반응형

Printing with Flex: Chapter 11 - Enterprise Development with Flex

by Yakov Fain, Anatole Tartakovsky, Victor Rasputnis
Enterprise Development with Flex book cover

This excerpt is from Enterprise Development with Flex. If you want to use Adobe Flex to build production-quality Rich Internet Applications for the enterprise, this groundbreaking book shows you exactly what's required. You'll learn efficient techniques and best practices, and compare several frameworks and tools available for RIA development -- well beyond anything you'll find in Flex tutorials and product documentation. Through many practical examples, the authors impart their considerable experience to help you overcome challenges during your project's life cycle.

buy button

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.
--Bill Gates

In general, the process of printing from web applications works a little differently compared to printing from the desktop. Web applications have good reason for not allowing direct access to a user’s printer: malicious websites could immediately start printing their fliers on your home or corporate printer, offering you anything from pizza delivery to adult entertainment. That’s why you can’t write a program in JavaScript that would automatically detect all available printers and send them print jobs. That’s why the user is forced to manually select the printer via the web browser’s pop-up dialog window.

Existing Flash Player bugs add more issues for Flex developers; for example, the Print dialog might not report all features of the available printer, and setting such parameters as tray selection or paper size might not be possible. To put it simply, you may not have complete control over the user’s printer from an application running in Flash Player. You may need to adjust your reports to standard printer settings.

Note

Adobe had a product called FlashPaper that tried to mitigate these limitations by adding ActionScript 2 objects to a special control with complete access to the printer. In 2008, however, Adobe discontinued FlashPaper, apparently promoting printing PDF documents using Acrobat instead.

The process of printing from Flash Player consists of starting a single-threaded print job and adding dynamically created pages to it (i.e., the data that comes from a database). Unfortunately, Flash Player’s virtual machine AVM2 ActionScript timeout is 15 seconds. Accordingly, for both Flex and AIR, the interval between the following commands shouldn’t be more than 15 seconds:

  • PrintJob.start() and the first PrintJob.addPage()

  • PrintJob.addPage() and the next PrintJob.addPage()

  • PrintJob.addPage() and PrintJob.send()

If, at each of these commands, printing the specified page always completed in 15 seconds or less, your application will be able to print a multipage document, although somewhat slowly. If any of the intervals spans more than 15 seconds, however, your print job will receive a runtime exception, which turns direct printing from Flash Player into an unpleasant experience, if application developers don’t handle exceptions properly. Plus, if the internal thread that started the print job failed, it may be automatically closed and unable to be recovered properly.

Note

You can read more about handling printing errors in the Adobe document “Flash Player and Air tasks and system printing”.

You may think that setTimeout() can help break the 15-second barrier for printing, but it can’t. Printing has to be handled by the same internal AVM2 thread (apparently a bug), and with setTimeout(), you are in fact spawning a new one. The issue with printing long documents is demonstrated in Example 11.1, “PrintTimeout.mxml—an illustration of printing failure”. The PrintJob starts and the method finishPrinting() is called in the same thread and works fine. If you instead comment out the call to finishPrinting() and uncomment the method setTimeout(), this printing job will fail: the addPage() will throw an exception, because it runs in a thread different than PrintJob.

Imagine that a timeout was initiated not by calling the function setTimeout(), but rather by Flash Player during printing of a multipage document because one of the addPage() calls took longer than 15 seconds. In this case, addPage() would be called on a different internal thread than PrintJob.start() and the addPage() operation would fail, even though Flash Player should’ve known how to process a such situation properly.

Example 11.1. PrintTimeout.mxml—an illustration of printing failure

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical">
   <mx:Button label="Print Me" click="printMe()"/>
   <mx:Script>
      <![CDATA[

         private function printMe() :void {
            var pj:PrintJob = new PrintJob();
            pj.start();

   //           setTimeout(function() :void { finishPrinting(pj);}, 1);

                   finishPrinting(pj);
         }

         private function finishPrinting(pj:PrintJob): void {
            pj.addPage(this);
            pj.send();
         }
      ]]>
   </mx:Script>
</mx:WindowedApplication>

Example 11.1, “PrintTimeout.mxml—an illustration of printing failure” just prints itself, addPage(this), but if it had to print, say, a slow-rendered DataGrid with a couple of thousand rows, the chances are high that such a program would time out before the printing job was finished.

There is a bigger problem than the technical restrictions mentioned so far, and it is in the very approach to printing via the PrintJob API. The process of programming reports in ActionScript comes down to creating snapshots of components displayed on the users’ monitors and sending them to the printer. Because screen resolution differs from printer resolution, however, application developers pursing this method need to create separate layouts just for printing, which is time-consuming and challenging.

That’s why you should consider creating and printing your reports as PDF files. Besides, it neatly reinforces this book’s philosophy: minimize the amount of code that business application developers have to write. In this chapter, you’ll learn how to create XDP-enabled Flex components that will allow application developers to generate PDF documents on the client side with minimal coding.

PDF Generation on the Server

PDF generation is supported by Adobe LiveCycle and LCDS, as well as other server-side products. Suppose that you have a Flex or AIR window with a number of UI controls, and you want to create a PDF out of it. One option is to create a snapshot of the Flex component or container using the class mx.graphics.ImageSnapshot and its function captureImage(), which can scale the image to a specific resolution and encode it into a specified image format. You can send an instance of this class via RemoteObject to the server with LCDS installed. LCDS then creates a PDF document (or merges it with a PDF form) and includes the new image received from the client.

The problem with this approach is that the resulting PDF will not be searchable. For instance, if a Flex Panel has text fields, you won’t be able to find the text of these fields in Acrobat Reader if the Panel is embedded as a bitmap.

Such PDFs have limitations on resolution as well (to create a PDF with resolution 300 dpi, you’d need to create a multimegabyte image). Also, printed materials often use different CSS and metrics from the screen ones. You don’t want to print, say, a background gradient that looks fine on the monitor, but bad on paper.

To embed forms into PDF documents, Adobe uses the XDP format. If you purchase an LCDS license, you’ll have the option to use it. You can design forms in Acrobat Designer and export the data from your Flex view, and LCDS will merge the data and the form on the server. On the Java side, LCDS adds several JARs in the lib directory of your web application, which makes the XFAHelper Java class available for your server-side PDF generation.

After generating the PDF, the server-side program can be:

  • Placed as a ByteArray in HTTPSession object

  • Saved as a file on the server for future use

  • Streamed back to the client marked as a MIME type application/pdf

  • Saved in a DBMS field as a binary large object (BLOB)

Depending on the business requirements, the server-side PDF generation might not be feasible. You might have just disconnected the AIR application, or the server software may not have any of the technologies supporting PDF creation installed. If the Flex UI is truly dynamic, that might change the number of displayed components based on some business criteria; developing an additional UI in Acrobat Designer just for printing can in these ways become either impossible or time-consuming. The LCDS Developer Guide describes this process in the document called “Using the PDF Generation Feature”.

Note

Adobe has published an article describing the process of creating PDF documents using templates.

In general, for server-side PDF generation from Adobe Flex applications, you have to do the following:

  • Use Adobe LiveCycle Designer ES, which provides tools for creating interactive forms and personalized documents (see http://www.adobe.com/products/livecycle/designer/). This software comes with Acrobat Professional or can be purchased separately, and is well documented, but it requires someone to create the XDP form and the data model and establish a process of synchronizing the Flex application with the server-side LiveCycle.

  • Initiate the server-side PDF generation from your Flex application seamlessly.

Although this process provides guaranteed quality and predictable results, it also requires the double effort of developing XDP forms for printing and Flex forms for displaying. Besides, LiveCycle Designer is another piece of software that application developers in your organization may not be familiar with.

LCDS generation with merging data and forms produces good printing quality with LCDS. The Flex client sends data as XML to the server along with the name of the form file (template) to be used for merging, as shown in Example 11.2, “Class FormRenderer.as”. In this case, the LCDS layer just needs to process it with the XDPXFAHelper class and return it as a PDF stream to the browser for displaying.

Note

Only commercial licenses of LCDS support PDF generation.

The ActionScript class FormRenderer sends generated XDP to the server and opens a web browser’s window to display the PDF when it arrives from the server.

Example 11.2. Class FormRenderer.as

import flash.net.*;
import flash.utils.ByteArray;

public class FormRenderer {
 public static function openPdf(xdp:String, target:String="_blank"):void{
   var req:URLRequest = new URLRequest("/createPDF.jsp");
   req.method = URLRequestMethod.POST;

   var ba :ByteArray = new ByteArray();;
   ba.writeMultiByte(xdp, "iso-8859-1");
   ba.compress();
   ba.position = 0;
   req.data = ba;
   navigateToURL(req, target);
 }
}

You also need an XDP file with the data and presentation. If you don’t have LiveCycle Designer, you can make the XDP file programmatically, ensuring that it matches the printer’s paper size and corporate stationery. XDP documents are XML objects, which are easily processed in Flex using E4X syntax, for example:

  1. Declare a variable of type XML, and initialize it with the required XDP template deployed on the server. A fragment of the XDP template may look like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <?xfa generator="AdobeLiveCycleDesigner_V8.0" APIVersion="2.5.6290.0"?>
     <xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2007-01-25T10:40:38Z"
      uuid="784f469b-2fd0-4555-a905-6a2d173d0ee1">
    
      <template xmlns="http://www.xfa.org/schema/xfa-template/2.5/">
       <subform name="form1" layout="tb" restoreState="auto" locale="en_US">
        <pageSet>
         <pageArea name="Page1" id="Page1">
          <contentArea x="0.25in" y="0.25in" w="8in" h="10.5in"/>
          <medium stock="letter" short="8.5in" long="11in"/>
          <?templateDesigner expand 1?></pageArea>
          <?templateDesigner expand 1?></pageSet>
    
          <subform w="8in" h="10.5in" name="YourPageAttachedHere"/>
          <proto/>
          <desc>
            <text name="version">8.0.1291.1.339988.308172</text>
          </desc>
       </subform>
      </template>
  2. Select a Flex UI container or component that you are going to print (a Panel, a DataGrid, and so on).

  3. Query the object from step 2, get its XDP attributes and children, and create the XML preparing this object for printing. Attach the XML to the template as a page.

Because original Flex components don’t know how to represent themselves in the XDP format, you’ll need to teach them to do so. This becomes the next task in enhancing Flex components.

For example, each UI component can implement some interface (e.g., IXdpObject with the only getter, xmlContent()) that allows it to return its own XDP content or, in the case of containers, to traverse the list of its child components for their XDP content. For example, a new panel component (PanelXdp) may have the following structure:

public class PanelXdp extends Panel implements IXdpObject{
    public function get xmlContent():Object{
       // The code to return representation of the panel
       // in the XDP format goes here
    }
}

Repeat the process of attaching XML to the XDP template using E4X until all print pages are ready. This method of printing from Flex requires less effort for reporting and creation of dynamic layouts. It might also provide better printing quality and searchability within the printed document.

Example 11.3, “Render.jsp” is the server-side part written as a Java ServerSide Page. It uncompresses the XDP stream received from the client, creates the PDF using XDPXFAHelper, turns it into an array of bytes, and sends it back to the client as the MIME type "application/pdf".

Example 11.3. Render.jsp

<%@ page language="java"
   import="java.io.*,
   java.util.*,
   javax.xml.parsers.*,
   org.w3c.dom.Document,
   flex.messaging.*,
   flex.acrobat.pdf.XDPXFAHelper,
   flex.messaging.util.UUIDUtils,
   org.w3c.dom.Document
   "
%><%!
private static void _log(Object o){
   System.out.println(""+o);
}
private String getParam(HttpServletRequest request, String name, String defVal)
throws Exception{
       String val = request.getParameter(name);
     return (val!=null && val.length()>0)?val:defVal;
}
private String getParam(HttpServletRequest request, String name) throws Exception{
   return getParam(request, name, null);
}
private void processRenderRequest(HttpServletRequest request,
                        HttpServletResponse response) throws Exception{

   String data      = getParam(request, "document");
   String template = getParam(request, "template"); // Security hole, check path
   _log("template="+template);
   _log("data="+data);
   template = FlexContext.getServletContext().getRealPath(template);
   _log("template real="+template);

       // You must have LCDS license to use XDPXFAHelper
   XDPXFAHelper helper = new XDPXFAHelper();
   helper.open(template);
   // Import XFA dataset
   if( data!=null ){
      _log("data.length="+data.length());
      ByteArrayInputStream bais = new
                        ByteArrayInputStream(data.getBytes("UTF-8"));
      DocumentBuilderFactory builderFactory =
                                    DocumentBuilderFactory.newInstance();
      DocumentBuilder builder =
                                    builderFactory.newDocumentBuilder();
      Document dataset = builder.parse(bais);
      helper.importDataset(dataset);
   } else
      _log("data="+null);

   byte[] content = helper.saveToByteArray();
   _log("content="+content);
   helper.close();
   ServletOutputStream out3 = response.getOutputStream();
   response.setContentType("application/pdf");
   response.setContentLength(content.length);
   out3.write(content);
}
%><%
_log("");
_log("--------------------------------------------");
_log("render.jsp");
processRenderRequest(request, response);
%>

Note

The WebORB PDF Generator from Midnight Coders allows you to either create XML printing templates on the server or generate them in Flex clients. To use this solution, you have to install the WebORB Server. For more details, visit http://www.themidnightcoders.com/products/pdf-generator/overview.html.

Now we’ll take a look at how to generate a PDF on the Flex side.

PDF Generation on the Client

AlivePDF is an open source library for generating PDFs in ActionScript on the client side. It’s offered under the MIT license at http://alivepdf.org; download AlivePDF.swc and link it to your Flex Builder project. One of the classes included in AlivePDF.swc is called PDF.

Note

Unless you are developing an AIR application or deploying it for Flash Player 10 (see the next note), even client-generated PDF content has to be sent to a server that will just bounce it back (see Example 11.20, “PDFServlet.java”) to have the web browser open the Acrobat Reader plug-in.

Basic Printing with AlivePDF

The process of generating PDFs in AlivePDF starts with instantiation of the PDF class, specifying the print orientation, units of measurement, and the paper size. Then you create and add pages to the instance of the PDF object, and finally you call the function savePdf() to turn these pages into a ByteArray and save them to the PDF file on your filesystem if you use Adobe AIR. If this is a web application written in Flex, the same savePdf() function sends the ByteArray to the server with the deployed script create.php (supplied by alivepdf.org), which will return this array of bytes to your web browser as a PDF document.

Note

Starting from Flash Player 10, the FileReference class allows you to save files locally. Its function save() opens the pop-up window, allowing the user to specify the filename for saving the data. In our example, this eliminates the need for a round trip to the server that does nothing but bounce this array of bytes. Keep in mind, though, that after saving the PDF this way, the user will need to complete an extra step to open the file with Acrobat Reader or any other program.

Example 11.4, “Basic printing with AlivePDF: test1.mxml” shows the process of preparing and saving a PDF file with AlivePDF.

Example 11.4. Basic printing with AlivePDF: test1.mxml

<?xml version="1.0" encoding="utf-8"?>
    <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
   layout="vertical" horizontalAlign="center" verticalAlign="middle">

   <mx:Button label="Hello World!!!" click="hw()"/>

 <mx:Script><![CDATA[

   import org.alivepdf.fonts.Style;
   import org.alivepdf.fonts.FontFamily;
    import org.alivepdf.saving.Method;
    import org.alivepdf.layout.Size;
    import org.alivepdf.layout.Unit;
        import org.alivepdf.layout.Orientation;
    import org.alivepdf.pdf.PDF;

  private function hw():void{

   var p:PDF = new PDF(Orientation.PORTRAIT, Unit.MM, Size.A4);
   p.addPage();
   p.setFont(FontFamily.ARIAL, Style.NORMAL, 12);
   p.addText("10x10", 10, 10);
   p.addText("100x100", 100, 100);
   p.addMultiCell(50, 8, "Hello World2");

   savePdf(p, "hw.pdf");
 }

 private function savePdf(p:PDF, fileName:String):void{

   var ba:ByteArray = p.save(Method.LOCAL);
   var fs:FileStream = new FileStream();
   var f:File = File.desktopDirectory.resolvePath(fileName);
   fs.open(f, FileMode.WRITE);

   try{
      fs.writeBytes(ba);
   } catch (e:*){}

     fs.close();
 }
]]></mx:Script>

</mx:WindowedApplication>

After you click the button Hello World!!! (see the example code), a file called hw.pdf is created in the AIR desktop directory (see Chapter 9, Working with Adobe AIR for details). For example, Figure 11.1, “Sample output of the AlivePDF program” shows our hw.pdf file, which was saved in the directory C:\Documents and Settings\Administrator\Desktop.

Figure 11.1. Sample output of the AlivePDF program

Sample output of the AlivePDF program

The goal here was to give you a taste of the process of preparing the document with AlivePDF. To investigate the complete set of AlivePDF’s APIs, visit http://AlivePDF.org.

AlivePDF does a good job of creating objects and assembling them into pages, which are then converted into a PDF format. But it still requires you to prepare (in addition to the screen version) a second copy of the UI to be printed. It’s not what-you-see-is-what-you-get (WYSIWYG) programming. This process requires manual allocation and measurements of each object in the PDF-to-be.

Note

The blog http://alivepdf.bytearray.org is yet another good source of up-to-date information regarding AlivePDF.

Enhancing AlivePDF

What can be done to improve this process? We still want to use AlivePDF’s printing engine, but we don’t want to manually write the code specifying styles and measurements as we did in Example 11.4, “Basic printing with AlivePDF: test1.mxml”. In this section, you’ll see how to create components and containers that are smart enough to present themselves as PDF or XDP objects.

Note

All examples from this section are located in the Flex Builder project called clientPdfAir (which comes with this chapter; see the Preface for information on obtaining the sample code) and are self-contained AIR applications. alivePDF.swc has to be present in the build path of the project.

The program test2.mxml in Example 11.5, “Printing with AlivePDF from an AIR application” illustrates Flex-to-PDF-object conversion, a big difference compared to test1.mxml. It uses the ActionScript class AlivePdfPrinter, which is included with the code samples of this chapter. Its addObject() method converts a given Flex object to corresponding PDF snippets, one at a time. You don’t need to worry about the sizes, locations, fonts, or styles of these objects. This is WYSIWYG. Flash Player is a rendering engine here.

Example 11.5. Printing with AlivePDF from an AIR application

<?xml version="1.0" encoding="utf-8"?>
    <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
     xmlns:printer="com.farata.printing.pdf.client.*"
   layout="vertical" >
   <mx:Style source="main.css"/>
   <mx:Canvas width="100%" height="100%" backgroundColor="white">
     <mx:Label id="lbl1" text="Hello" x="10" y="10"/>
     <mx:Label id="lbl2" text="World" x="50" y="30" fontWeight="bold"/>
          <mx:Label id="lbl3" text="And" x="150" y="60" fontStyle="italic"
                                                        enabled="false"/>
     <mx:Label id="lbl4" text="Happy" x="70" y="90" fontSize="16"
                                         textDecoration="underline"/>
         <mx:Label id="lbl5" text="New Year" x="50" y="140" fontSize="24"
                                        fontWeight="bold" color="green"/>
     <mx:Button id="btn1" label="Button1" x="70" y="240"/>
   </mx:Canvas>

 <mx:ApplicationControlBar width="100%">
   <mx:Label text="File name:"/>
   <mx:TextInput id="txtFileName" text="hw2.pdf"/>
   <mx:Button label="Save PDF" click="printPdf()"/>
     </mx:ApplicationControlBar>

   <printer:AlivePdfPrinter id="prn" printComplete="viewPdf()"/>
<mx:Script><![CDATA[

  import flash.net.URLRequest;

  private var file:File;

  private function printPDF():void{

   prn.addObject(lbl1);
   prn.addObject(lbl2);
   prn.addObject(lbl3);
   prn.addObject(lbl4);
   prn.addObject(lbl5);
   prn.addObject(btn1);

   file = prn.printToFile (txtFileName.text);
 }

 private function viewPdf():void{
   var req:URLRequest = new URLRequest(file.url);
   navigateToURL(req, "_blank");
}

]]></mx:Script>

</mx:WindowedApplication>

The code in Example 11.5, “Printing with AlivePDF from an AIR application” produces the screen as it’s shown in the AIR runtime, on the left in Figure 11.2, “The results of printing with enhanced AlivePDF”. On the right side, you can see the hw2.swf file produced by this program and shown in Adobe Acrobat. The fonts in the Acrobat Reader image look smaller because of the small zoom percentage.

Figure 11.2. The results of printing with enhanced AlivePDF

The results of printing with enhanced AlivePDF

The listings that follow do not include the entire source code of the class AlivePdfPrinter; that comes with the source code of this chapter’s samples. The idea is to have a set of components that can expose their information in a form suitable for printing. The method AlivePdfPrinter.addObject(o) calls locateExtension(), which instantiates the appropriate object that can present itself in a form suitable for AlivePDF (Example 11.6, “The method AlivePdfPrinter.locateExtension()”).

Example 11.6. The method AlivePdfPrinter.locateExtension()

private static function locateExtension(o:*):IPdfPrinterExtension{
  if( o is Label )
   return new LabelPdfExtension(/*o*/);
  if( o is PdfPrintDataGrid )
   return new PdfPrintDataGridPdfExtension(/*o*/);
  if( o is DataGrid )
   return new DataGridPdfExtension(/*o*/);
  if( o is Container )
   return new ContainerPdfExtension(/*o*/);
  if( o is UIComponent )
   return new UIComponentPdfExtension(/*o*/);
 return null;
}

After identifying the type of the object to be printed, the object exposes its font and style information that’s being passed to the AlivePDF’s PDF object for printing. Example 11.7, “The addObject() method in the LabelPdfExtension class” shows the function addObject() from the ActionScript class com.farata.printing.pdf.client.extensions.LabelPdfExtension.

Example 11.7. The addObject() method in the LabelPdfExtension class

public function addObject(o:*, printer:IAlivePdfPrinter):void{
 var pdf:PDF = printer.pdf;
   var c:Label = Label(o);
   if( c==null ) return;
   if( !c.visible )
      return;

   var fontFamily:String = c.getStyle("fontFamily");
   if( fontFamily==null )
      fontFamily = FontFamily.ARIAL;

   var style:String = "";

   if( c.getStyle("fontWeight")=="bold" )
       style += "B";

   if( c.getStyle("fontStyle")=="italic" )
      style += "I";
   if( c.getStyle("textDecoration")=="underline" )
        style += "U";

   var size:int = c.getStyle("fontSize");
   var color:Color = new RGBColor(c.getStyle(c.enabled?"color":"disabledColor"));

   allocateSpace(c, printer);

   pdf.textStyle(color/*, alpha*/);
   pdf.setFont(fontFamily, style, pxToPt(size));

   var ptText:PPoint = mapPoint(c, printer);
   ptText.y.px += c.height;

   pdf.addText(c.text, ptText.x.pt, ptText.y.pt);

 }

Example 11.7, “The addObject() method in the LabelPdfExtension class”’s code gets the styles, font, and text from the Flex Label object and initializes appropriate properties of the PDF object per the requirements of the AlivePDF library.

The sample code of this chapter as well as the clear.swc library has several similar extensions for a number of Flex components (see com.farata.printing.swc), and you can keep adding more objects to your own business framework of PDF-ready components.

These extensions are not subclasses of corresponding Flex components, but rather utility classes that know how to present the content of components to AlivePDF.

Note

While writing the method addObjects() for your components, keep in mind that measurements in Flash Player are in pixels and you may need to convert them into other units required by the AlivePDF API.

If you’d like to see what’s inside the generated hw2.pdf file, just open it with any text editor; you’ll see something like Example 11.8, “A fragment of the h2.pdf content” (which is just a fragment of the file).

Example 11.8. A fragment of the h2.pdf content

%PDF-1.4
1 0 obj
<</Type /Pages
/Kids [3 0 R
]
/Count 1
>>
endobj
3 0 obj
<</Type /Page
/Parent 1 0 R
/MediaBox [0 0 595.28 841.89]
/Resources 2 0 R
/Rotate 0
/Dur 3
/Contents 4 0 R>>
endobj
4 0 obj
<</Length 780>>
stream
2 J
0.57 w
0 Tr
/GS0 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F1 7.00 Tf ET
q 0.043137254901960784 0.2 0.23529411764705882 rg BT 25.50 802.14 Td (Hello) Tj ET
Q
0 Tr
/GS1 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F2 7.00 Tf ET
q 0.043137254901960784 0.2 0.23529411764705882 rg BT 55.50 787.89 Td (World) Tj ET
Q
0 Tr
/GS2 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F3 7.00 Tf ET
q 0.6666666666666666 0.7019607843137254 0.7019607843137254 rg BT 130.50 764.64 Td
(And) Tj ET Q
0 Tr
/GS3 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F1 12.00 Tf ET
q 0.043137254901960784 0.2 0.23529411764705882 rg BT 70.50 738.39 Td (Happy) Tj ET
70.50 737.19 34.68 0.60 re f Q
0 Tr
/GS4 gs
0 Tw 0 Tc 100 Tz 0 TL
BT /F2 18.00 Tf ET
q 0 0.5019607843137255 0 rg BT 55.50 692.64 Td (New Year) Tj ET Q
/GS5 gs
q 47.25 0 0 16.50 52.50 645.39 cm
/I1 Do Q
endstream
endobj
5 0 obj
<</Type /ExtGState
/SA true
/CA 1
/n 5
/BM /Normal
/ca 1
>>
endobj
...
15 0 obj
<<
/Producer (Alive PDF 0.1.4.6)
/CreationDate (D:200905152226)
>>
endobj
16 0 obj
<<
/Type /Catalog
/Pages 1 0 R
/OpenAction [3 0 R /FitH null]
/PageLayout /SinglePage
>>
endobj
xref
...
trailer
<<
/Size 17
/Root 16 0 R
/Info 15 0 R
>>
startxref
2467
%%EOF

Note

After this chapter was written, a new open source library called purePDF became available. It's an ActionScript port of a popular Java library called iText. You can download purePDF at http://code.google.com/p/purepdf/.

Printing Flex Containers

All these extensions for Flex controls are great, but there is another issue to tackle: Flex views often use containers. For example, you need to be able to generate a PDF for a DataGrid in two formats. This object should be similar to mx.printing.PrintDataGrid, but it should support PDF printing rather than working with PrintJob. It should support pagination, headers, and footers; this is a must for printing multipage documents.

Or imagine a TabNavigator container from which you need to print the content of each tab as a separate multipage PDF. The goal is to have a container that can iterate its children and tell each of them, “Hey, kiddo, print yourself.” When this mission is accomplished, just implement the same behavior to allow containers (and components) to expose themselves in the XDP format, too.

Sample extensions for the most complex Flex components, such as DataGrid, DataGridItemRenderer, and Canvas, are supplied as code samples for this chapter. Use them as a guide for the creation of your own printing extensions.

For example, the application test4.mxml includes the PdfPrintDataGrid component from clear.swc and outputs the data grid to the file hw4.pdf, as shown in Figure 11.3, “Running test4.mxml (left) generates PDF hw4 (right)” and Example 11.9, “An AIR application to print a data grid to a PDF file”.

Figure 11.3. Running test4.mxml (left) generates PDF hw4 (right)

Running test4.mxml (left) generates PDF hw4 (right)

Example 11.9. An AIR application to print a data grid to a PDF file

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
  xmlns:printer="com.farata.printing.pdf.client.*"
  layout="vertical"
  creationComplete="onCreationComplete(event)" >

   <mx:DataGrid id="dg" x="0" y="0"
   width="100%" height="100%" >

    <mx:columns>
      <mx:DataGridColumn dataField="name" headerText="Name"/>
      <mx:DataGridColumn dataField="phone" headerText="Phone"/>
      <mx:DataGridColumn dataField="email" headerText="E-mail"/>
    </mx:columns>
   </mx:DataGrid>

   <mx:ApplicationControlBar width="100%">
    <mx:Label text="File name:"/>
    <mx:TextInput id="txtFileName" text="hw4.pdf"/>
     <mx:Button label="Save PDF" click="doSavePdf()"/>
   </mx:ApplicationControlBar>

   <printer:AlivePdfPrinter id="prn" printComplete="viewPdf()"/>

<mx:Script><![CDATA[

import flash.net.URLRequest;
private var file:File;

private function onCreationComplete(evt:*):void{

   var array:Array = [];

   for(var i:int=1; i<=30; i++){
       var obj:Object = new Object();
      obj.name   = "User " +i;
      obj.phone  = "555-219-227"+i;
      obj.email  = "user"+i+"@hello.world";
      obj.active = (i % 2 ) == 1;
      array.push(obj);

   }
   dg.dataProvider = array;
}
private function printPdf():void{

   prn.addObject(dg);
   file = prn.printToFile(txtFileName.text);
}

private function viewPdf():void{
   var req:URLRequest = new URLRequest(file.url);
   navigateToURL(req, "_blank");
}

]]></mx:Script>
</mx:WindowedApplication>

The line:

prn.addObject(dg);

results in invoking the code from Example 11.6, “The method AlivePdfPrinter.locateExtension()”, and the DataGridPdfExtension class shown in Example 11.10, “Class DataGridPdfExtension” is engaged.

Example 11.10. Class DataGridPdfExtension

package com.farata.printing.pdf.client.extensions{

   import com.farata.printing.PdfPrintDataGrid;

   import com.farata.printing.pdf.client.IAlivePdfPrinter;
   import mx.controls.DataGrid;

   public class DataGridPdfExtension extends BasePdfExtension{

    override public function addObject(o:*, printer:IAlivePdfPrinter):void{

     var c:DataGrid = o as DataGrid;
      var p:PdfPrintDataGrid = new PdfPrintDataGrid();
      p.x = c.x;
       p.y = c.y;
     p.width = c.width;
     p.height = c.height;
     p.columns = c.columns;
     p.dataProvider = c.dataProvider;

      c.parent.addChild(p);
     printer.addObject(p);
     c.parent.removeChild(p);
   }

 }
}

If in Example 11.6, “The method AlivePdfPrinter.locateExtension()” all components were located inside, say, Canvas, and the printing extensions for this container were ready, this code sample would become even shorter—something like this:

prn.addObject(myCanvas);

The myCanvas component would’ve taken care of its kids.

The good news is that you don’t have to write printing extensions to all components. The code in Example 11.6, “The method AlivePdfPrinter.locateExtension()” checks to see whether the component is an instance of Label, DataGrid, or Container.

Part of the sample code in the test3.mxml application has a canvas:

<mx:Canvas id="canvas" width="100%" height="100%"
      backgroundColor="white">

   <mx:Label id="lbl1" text="Hello" x="100" y="10"/>
    <mx:Label id="lbl2" text="World" x="50" y="30" fontWeight="bold"/>
    <mx:Label id="lbl3" text="And" x="150" y="60" fontStyle="italic"
              enabled="false"/>
    <mx:Label id="lbl4" text="Happy" x="90" y="90" fontSize="16"
                                      textDecoration="underline"/>
   <mx:Label id="lbl5" text="New Year" x="80" y="140" fontSize="24"
     fontWeight="bold" color="green"/>
   <mx:Button id="btn1" label="Button1" x="1" y="1"/>
    <mx:Button id="btn2" label="Button2" x="10" y="100"/>

    <mx:HBox x="250" y="130" borderThickness="3" borderColor="blue"
       borderStyle="solid"   backgroundColor="yellow">
      <mx:Label text="Inside HBox" color="gray"/>
   </mx:HBox>
</mx:Canvas>

The code to print this Canvas is pretty simple: just pass a reference to the class AlivePdfPrinter, and it’ll figure out how to print its child components:

private function printPdf():void{
   prn.addObject(canvas);
   file = prn.printToFile(txtFileName.text);
}

The function addObject() tries to locate an extension class for Canvas as shown in Example 11.6, “The method AlivePdfPrinter.locateExtension()”, and will use the ContainerPdfExtension, because Canvas is a Container. Should you want to provide some functionality specific to Canvas, you need to create CanvasPdfExtension and modify the code in Example 11.6, “The method AlivePdfPrinter.locateExtension()” accordingly.

A fragment of ContainerPdfExtension is shown in Example 11.11, “The main part of ContainerPdfExtension”.

Example 11.11. The main part of ContainerPdfExtension

package com.farata.printing.pdf.client.extensions{
 public class ContainerPdfExtension extends BasePdfExtension{

   private static var s_offsetLock:int = 0;

   override public function addObject(o:*, printer:IAlivePdfPrinter):void{

    s_offsetLock++;
    var c:Container = Container(o);
    setOffset(c, printer);
    allocateSpace(c, printer);
    drawBackgroundAndBorder(c, printer);

     var len:int = c.numChildren;
    for(var i:int=0; i<len; i++){
      var ch:DisplayObject = c.getChildAt(i);
      printer.addObject(ch);
    }

    s_offsetLock--;
    }

   private  function setOffset(o:Container, printer:IAlivePdfPrinter):void{
     if( s_offsetLock==1 ) {
      var ptLocal:Point = new Point(o.x, o.y);
        var ptGlobal:Point = o.parent.localToGlobal(ptLocal);
        printer.lastOffset.x.px = ptGlobal.x;
        printer.lastOffset.y.px = ptGlobal.y;
     }
    }
 }

}

All other components that the code in Example 11.6, “The method AlivePdfPrinter.locateExtension()” won’t recognize will be printed as prescribed in the UIComponentPdfExtension as a snapshot of an image (Example 11.12, “Printing a Flex object as an image”).

Example 11.12. Printing a Flex object as an image

public function addObject(o:*, printer:IAlivePdfPrinter):void{

   if( !o.visible ) return;
   var c:UIComponent = o;
   var pdf:PDF = printer.pdf;
   var rc:PRectangle = new PRectangle();
   rc.left.px = c.x;

   rc.top.px = c.y;
   rc.right.px = rc.left.px+c.width;
   rc.bottom.px = rc.top.px+c.height;

   printer.allocateSpace(rc);
   pdf.addImage(c, rc.left.pt, rc.top.pt, rc.right.pt-rc.left.pt,
   rc.bottom.pt-rc.top.pt);
}

Of course, it’s better not to use a bitmap but instead a PDF representation specific to a component, which will allow Acrobat Reader to recognize its text content and generate searchable documents rather than bitmaps.

Extending Flex Components for PDF Generation in XDP Format

In this section, you’ll learn how to enhance standard Flex UI components so that they can properly present themselves for rendering as PDF-friendly objects in the XML-based XDP format.

The ActionScript code snippet in Example 11.13, “Representing a CheckBox as an XDP checkButton” shows how you can present a checkbox as an element of the PDF form in XDP format (in XDP, a checkbox is called checkButton).

We’ll introduce a new interface, IXdpObject, and each of our enhanced UI components will implement it to return properly prepared XML to represent itself. This will allow you to turn the entire Flex view into a searchable PDF.

Example 11.13, “Representing a CheckBox as an XDP checkButton” is an example of implementing the getter xdpContent() defined in the IXdpObject interface to produce a CheckBox in the XDP format.

Example 11.13. Representing a CheckBox as an XDP checkButton

// IXdpObject interface implementation
public  function get xdpContent():Object {
 var o:XML =
      <field  x={convert(x)} w={convert(width)} h={convert(height)}>
   <ui>
     <checkButton  allowNeutral="1">
       <border>
         <edge stroke="lowered"/>
         <fill/>
       </border>
      </checkButton>
   </ui>
   <value>
     <text>{value}</text>
   </value>
   <para vAlign="middle" hAlign="center"/>

   <items>
     <text>{onValue}</text>
     <text>{offValue}</text>
     <text></text>
   </items>
   <caption placement="bottom"/>
   </field>;

 return o;
}

private function convert(value:Number) : String {
   return  XdpUtil.px2pt(value) + "pt";
}

This code snippet includes a getter, xdpContent, that returns the representation of our CheckBox in XDP format. It uses a helper function, convert(), to convert the value from pixels to points.

Note

Note that this code uses binding to insert the onValue and offValue variables that were introduced in Chapter , Building an Enterprise Framework in Example 55, “CheckBox with value and text properties”.

To generate a PDF for a particular Flex view, you need to loop through its children (every UI control of each container) and get each one’s xdpContent. If it’s not null, add its value (XDP) to the output file. If it does not have xdpConent, just get an image snapshot of this child and add it to the output file.

At the end of this process, you’ll get a mix of images and XDP content. If this is a Flex application, send this content to the server-side Java Servlet, which will sandwich it between the PDF header and footer. Voilà! Your PDF file is ready.

Obsessed with the mantra “Developers must write less code,” we at Farata have already created a number of classes in the package com.farata.printing that allows Flex components to expose themselves in a form of XDP.

The sample application shown in Example 11.14, “Saving data in XDP format: test_xdp2.mxml” is a rewrite of Example 11.5, “Printing with AlivePDF from an AIR application”. It’ll produce the same output as in Figure 11.2, “The results of printing with enhanced AlivePDF”, but this time the document will be encoded in the XDP format.

Example 11.14. Saving data in XDP format: test_xdp2.mxml

<?xml version="1.0" encoding="utf-8"?>

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 xmlns:local="*"
 xmlns:printer="com.farata.printing.pdf.client.*" layout="vertical">

   <mx:Style source="main.css"/>
   <mx:Canvas id="canvas" width="100%" height="100%"
      backgroundColor="white">
      <mx:Label id="lbl1" text="Hello" x="10" y="10"/>
      <mx:Label id="lbl2" text="World" x="50" y="30"
         fontWeight="bold"/>
           <mx:Label id="lbl3" text="And" x="150" y="60"
          fontStyle="italic" enabled="false"/>

      <mx:Label id="lbl4" text="Happy" x="70" y="90" fontSize="16"
              textDecoration="underline"/>
      <mx:Label id="lbl5" text="New Year" x="50" y="140" fontSize="24"
              fontWeight="bold" color="green"/>
      <mx:Button id="btn1" label="Button1" x="70" y="240"/>
   </mx:Canvas>

    <mx:ApplicationControlBar width="100%">
    <mx:Label text="File name:"/>
     <mx:TextInput id="txtFileName" text="hw2.pdf"/>
    <mx:Button label="Save PDF" click="savePdf()"/>
   </mx:ApplicationControlBar>

   <mx:Script>
      <![CDATA[
      import com.farata.printing.PrintOptions;
        import com.farata.printing.pdf.xdp.XdpDocument;
        import com.farata.printing.pdf.buffered.PDFHelper;

     private function savePdf():void{
      saveToFile(txtFileName.text, createXdpContent());
      }

     private function createXdpContent ():ByteArray{

      var xdpDocument:XdpDocument=new XdpDocument();
      xdpDocument.init(new PrintOptions());
      var pdf:PDFHelper=new PDFHelper(xdpDocument);

      pdf.createPDFPrologue();
      pdf.createPage(canvas, PDFHelper.TYPE_PAGE);
      pdf.createPDFEpilogue();

        return pdf.pdfContent;
   }

   private function saveToFile (file:String, ba:ByteArray):void{

      var fs:FileStream=new FileStream();
      var f:File=File.desktopDirectory.resolvePath(file);
      fs.open(f, FileMode.WRITE);

      try {
        fs.writeBytes(ba);
      } catch(e:*){
             // Process I/O errors here
       }
        fs.close();
   }

      ]]>
   </mx:Script>
</mx:WindowedApplication>

When you open the generated file h2.pdf in a text editor, notice that it looks different than the file shown in Example 11.8, “A fragment of the h2.pdf content”. The small PDF header and the trailer are there, but the main content of this file is in XDP format, as shown in Example 11.15, “A fragment of the h2.pdf content in XDP format”.

Example 11.15. A fragment of the h2.pdf content in XDP format

 %PDF-1.7
1 0 obj
<</Type /Catalog /StructTreeRoot 9 0 R /MarkInfo <</Marked true>> /Pages 15 0 R
/AcroForm 16 0 R /NeedsRendering true>>
endobj
2 0 obj
<</Type /Page /MediaBox [0 0 612 792] /Resources 5 0 R /Contents 4 0 R
/StructParent 0 /StructParents 0 /Parent 15 0 R>>
endobj
4 0 obj
<</Length 298>>
stream
BT
/Content <</MCID 0>> BDC
0.0 0.0 0.0 rg
/RelativeColorimetric ri
/T1_0 1.0 Tf
10.0 0.0 0.0 10.0 72.0 720.0 Tm
(Warning: This form is not supported at all with the current version of Acrobat or
Adobe Reader.) Tj
0.0 -1.8 Td
(Upgrade to the latest version for full support.) Tj
0.0 -1.8 Td
EMC
ET
endstream
endobj
5 0 obj
<</Font 8 0 R /ProcSet [/PDF /Text]>>
endobj
6 0 obj
<</Type /Encoding /BaseEncoding /WinAnsiEncoding>>
endobj
7 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding 6 0 R>>
endobj
8 0 obj
<</T1_0 7 0 R>>
endobj
9 0 obj
<</Type /StructTreeRoot /K 10 0 R /ParentTree 13 0 R /ParentTreeNextKey 1 /RoleMap
14 0 R>>
endobj
10 0 obj
<</S /Document /P 9 0 R /K 11 0 R>>
endobj
11 0 obj
<</S /Div /P 10 0 R /K 12 0 R>>
endobj
12 0 obj
<</S /P /P 11 0 R /Pg 2 0 R /K 0>>
endobj
13 0 obj
<</Nums [0 [12 0 R]]>>
endobj
14 0 obj
<</Field /Div /Subform /Sect /Page /Part /Draw /Div>>
endobj
15 0 obj
<</Type /Pages /Kids [2 0 R] /Count 1>>
endobj
16 0 obj
<</Fields [] /XFA 17 0 R>>
endobj
17 0 obj
<< /Length 18 0 R >>
stream
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
  <template xmlns="http://www.xfa.org/schema/xfa-template/2.5/">
    <subform name="doc1" layout="tb" restoreState="auto" locale="en_US">
      <proto/>
      <desc>
        <text name="version">8.0.1291.1.339988.308172</text>
      </desc>
      <pageSet>
        <pageArea name="Page1" id="Page1">
          <contentArea x="8.47mm" y="8.47mm" w="262.43mm" h="198.94mm"/>
          <medium stock="custom" short="215.87mm" long="279.37mm"
                                              orientation="landscape"/>
        </pageArea>
      </pageSet>
    <subform layout="tb" name="Subform1">
  <subform name="Container1" x="6.35mm" y="6.35mm" w="119.58mm" h="70.9mm">
    <draw x="7.5pt" y="7.5pt" w="22.5pt" h="14.25pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>Hello</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="7.5pt" weight="normal" posture="normal"
                   underline="0">
        <fill>
          <color value="11,51,60"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="37.5pt" y="22.5pt" w="24.75pt" h="13.5pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>World</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="7.5pt" weight="bold" posture="normal"
                                                          underline="0">
        <fill>
          <color value="11,51,60"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="112.5pt" y="45pt" w="18pt" h="14.25pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>And</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="7.5pt" weight="normal" posture="italic"
                                                 underline="0">
        <fill>
          <color value="11,51,60"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="52.5pt" y="67.5pt" w="37.5pt" h="18pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>Happy</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="12pt" weight="normal" posture="normal"
                                              underline="1">
        <fill>
          <color value="11,51,60"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="37.5pt" y="105pt" w="83.25pt" h="26.25pt">
      <ui>
        <textEdit hScrollPolicy="off" multiLine="0" vScrollPolicy="off"/>
      </ui>
      <value>
        <text>New Year</text>
      </value>
      <para hAlign="left"/>
      <font typeface="Arial" size="18pt" weight="bold" posture="normal"
underline="0">
        <fill>
          <color value="0,128,0"/>
        </fill>
      </font>
      <border>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
        <edge presence="hidden"/>
      </border>
    </draw>
    <draw x="18.52mm" y="63.49mm" w="16.67mm" h="5.82mm">
      <ui>
        <imageEdit/>
      </ui>
      <value>
        <image
contentType="image/png">iVBORw0KGgoAAAANSUhEUgAAAD8AAAAWCAYAAAB3/EQhAAABqElE
QVR42uWYTUsCURSGz76f07bfEtS2UkioqAgJKaMPERISQkJCqIggF2VItAsRi/5AhS11/BpH8YuT
rysb7jj35m7uhQfuOXPmnPvALGaGeLgaDZNz+cKIh6esZ4Ff4f1j5ItFvV6Ps88v/Pn1zeWywV5e
8IMnfOFNr7k85wtvPBgMtAG+8Kbr2zvu9/vaAW9KXd2MHgHdgDclL1Pc7Xa1A96UuEhyp9PRDnhT
/DzB7XZbmZnZuT/I1Iv20zBNH3hT7CzOlmUpg8GT4kn1brWy86fpA2+Knsa42Wwqg8GiWJR3QlTj
dJ/MLBXgTceRKJumqYz9cON5e509r1ovip1yssCbwodHw9e9hjIYLIpl8k57mT5uOVngTaH9MNfr
dWUwWBTL5J32Mn3ccrLAm4K7Ia7VasrYH3u3a057e/14zt5TdIb/nB3Am7Z3glytVrUD3rSxucWV
SkU74E2BtXUt5eFN/tUAG4ahHfCmpRXf8CO/rB3wpuOTCN+n01qJwxfeVCz+8LLPz4+ZDJdKJc8D
T/jCm/Bvy2q1eC98wPMLi54HnvDF+gUhlFFaqhacWgAAAABJRU5ErkJggg==</image>
      </value>
    </draw>
  </subform>
</subform></subform>
</template>

...

</xdp:xdp>
endstream
endobj
18 o obj

endobj
xref
0 19
0000000000 65535 f
0000000016 00000 n
0000000151 00000 n
0000000000 65535 f
0000000287 00000 n
0000000635 00000 n
0000000688 00000 n
0000000754 00000 n
0000000838 00000 n
0000000869 00000 n
0000000976 00000 n
0000001028 00000 n
0000001076 00000 n
0000001127 00000 n
0000001166 00000 n
0000001236 00000 n
0000001292 00000 n
0000001335 00000 n
trailer
<</Root 1 0 R  /Size 19>>
startxref
%%EOF

Note

The file templates/generic.xdp in com.farata.printing contains the generic template for XDP generation used for generation of h2.pdf and all other XDP samples from this chapter.

As you can see from Example 11.15, “A fragment of the h2.pdf content in XDP format”, the values of the text fields (Hello, World, etc.) are represented as XML text fields, which makes this PDF searchable. Note that the binary image is also presented in the encoded form as one of the XML elements.

Note

If you are developing not AIR but Flex applications, the client-side code can generate the entire XML portion of the Flex view components and send this XML to the server, where, say, Java Servlet puts it between the required PDF header and trailer and returns the entire document back to the web browser for printing.

The entire process of generation of this PDF in the XDP format is done by the following code from Example 11.14, “Saving data in XDP format: test_xdp2.mxml”:

var xdpDocument:XdpDocument=new XdpDocument();
xdpDocument.init(new PrintOptions());
var pdf:PDFHelper=new PDFHelper(xdpDocument);

pdf.createPDFPrologue();

pdf.createPage(canvas, PDFHelper.TYPE_PAGE);

pdf.createPDFEpilogue();

This code uses the helper components XdpDocument, PrintOptions, and PDFHelper, which are located in com.farata.printing.swc. The class PrintOptions is just a holder of such page parameters as orientation, margins, page size, and the like.

The MXML component XdpDocument implements a generic getter xdpContent, introduced in the beginning of this section. The source code of XDPDocument.mxml and a fragment of PDFHelper.as are shown in Examples 11.16 and 11.17, respectively. But these constitute just the tip of the iceberg, as they use uses dozens of supporting classes in the process of creation of the XDP content.

The good news is that unless the XDP format changes, you don’t need to learn all the nitty-gritty details, as we already did that tedious work of ensuring that the document is generated as required by the XDP specifications.

Example 11.16. Component XDPDocument.mxml

<?xml version="1.0" encoding="utf-8"?>

<xdp:XdpBaseObject
     xmlns:mx="http://www.adobe.com/2006/mxml"
     xmlns:xdp="com.farata.printing.pdf.xdp.*">

    <mx:XML id="xmlGen" source="/templates/generic.xdp"/>

<mx:Script><![CDATA[

import mx.core.UIComponent;

import com.farata.printing.pdf.buffered.PDFHelper;
    import com.farata.printing.geom.PNumber;
import mx.core.Container;
import com.farata.printing.geom.PSize;
import com.farata.printing.PrintOptions;
import com.farata.printing.geom.PRectangle;
import com.farata.printing.PaperSize;

public static var ns_xdp     : Namespace = new
Namespace("http://ns.adobe.com/xdp/");

public static var ns_xfat25  : Namespace = new Namespace(
                                          "http://www.xfa.org/schema/xfa-template/2.5/");

public static var ns_xci10   : Namespace = new
Namespace("http://www.xfa.org/schema/xci/1.0/");

public static var ns_xfals21 : Namespace = new
Namespace("http://www.xfa.org/schema/xfa-locale-set/2.1/");

public var paperSize   : PaperSize;
    public var margins     : PRectangle;
public var pageSize    : PSize;
public var orientation : String;
public var header:UIComponent;
public var footer:UIComponent;

public function get pages():Array{
   return children;
}

public override function get xdpContent():Object{

   var x:Object = xmlGen.copy();
   var f:Object = x..ns_xfat25::subform.(@name=="doc1")[0];
   var p:XML = <pageSet>
       <pageArea name="Page1" id="Page1">
      </pageArea>
   </pageSet>;

   var contentAreaX:Number = margins.left.px;
   var contentAreaY:Number = margins.top.px;
   var contentAreaH:Number = pageSize.height.px;
   var contentAreaW:Number = pageSize.width.px;

   if (header){
      var xdpHeader:XdpPage = new XdpPage();
      PDFHelper.createXdpPage(xdpHeader, header);

      xdpHeader.x = margins.left;
      xdpHeader.y = margins.top;
      contentAreaY = contentAreaY + header.height;
      contentAreaH = contentAreaH - header.height;

      p.pageArea.appendChild(xdpHeader.xdpContent);
   }



   if (footer){
      var xdpFooter:XdpContainer = new XdpContainer();
      PDFHelper.createXdpPage(xdpFooter, footer);
      xdpFooter.x = margins.left;
      var y:Number = pageSize.height.px + margins.top.px - footer.height;
      xdpFooter.y = new PNumber(y, PNumber.UNIT_PX);
      contentAreaH = contentAreaH - footer.height;

      p.pageArea.appendChild(xdpFooter.xdpContent);
   }

   p.pageArea.contentArea.@x = _pos(new PNumber(contentAreaX, PNumber.UNIT_PX));
   p.pageArea.contentArea.@y = _pos(new PNumber(contentAreaY, PNumber.UNIT_PX));
   p.pageArea.contentArea.@w = _pos(new PNumber(contentAreaW, PNumber.UNIT_PX));
   p.pageArea.contentArea.@h = _pos(new PNumber(contentAreaH, PNumber.UNIT_PX));

   p.pageArea.medium.@stock = "custom";
   p.pageArea.medium.@short = _pos(paperSize.width);
   p.pageArea.medium.@long  = _pos(paperSize.height);

   if( orientation==PrintOptions.ORIENTATION_LANDSCAPE )
      p.pageArea.medium.@orientation = "landscape";

   p.setNamespace(ns_xfat25);
   f.appendChild(p);
   f = applyStdData(f);

   return x;
}

public function addPage(p:XdpPage):void{

   addChild(p);
   p.pageNumber = pages.length;
   p.w = pageSize.width;
   p.h = pageSize.height;
}

public function init(opt:PrintOptions):void{

   paperSize = opt.paperSize.copy();
   margins = opt.margins.copy();

   pageSize = opt.pageSize;
   orientation = opt.orientation;
}
]]></mx:Script>

</xdp:XdpBaseObject>

The ActionScript class PDFHelper has about 300 lines of code; you can see some fragments of it in Example 11.17, “Fragments of PDFHelper.as”. We don’t provide code explanations here, as teaching the internals of the XDP protocol is not the goal of this chapter.

Example 11.17. Fragments of PDFHelper.as

package com.farata.printing.pdf.buffered{

   public class PDFHelper{


       private static var prefix : Array =[["\%PDF-1.7"+ "\n"," 65535 f"],
         ["1 0 obj"+"\n"+
         "<</Type /Catalog /StructTreeRoot 9 0 R /MarkInfo <</Marked true>> /Pages
15 0 R /AcroForm 16 0 R /NeedsRendering true>>"+"\n"+
         "endobj"+"\n", " 00000 n"],
         ["2 0 obj"+"\n"+
         "<</Type /Page /MediaBox [0 0 612 792] /Resources 5 0 R /Contents 4 0 R
/StructParent 0 /StructParents 0 /Parent 15 0 R>>"+"\n"+
         "endobj"+"\n", " 00000 n"],
         [""," 65535 f"],
         ["4 0 obj"+"\n"+
         "<</Length 298>>"+"\n"+
         "stream"+"\n"+
         "BT"+"\n"+
         "/Content <</MCID 0>> BDC"+"\n"+
         "0.0 0.0 0.0 rg"+"\n"+
         "/RelativeColorimetric ri"+"\n"+
         "/T1_0 1.0 Tf"+"\n"+
         "10.0 0.0 0.0 10.0 72.0 720.0 Tm"+"\n"+
         "(Warning: This form is not supported at all with the current version of
Acrobat or Adobe Reader.) Tj"+"\n"+
         "0.0 -1.8 Td"+"\n"+
         "(Upgrade to the latest version for full support.) Tj"+"\n"+
         "0.0 -1.8 Td"+"\n"+
         "EMC"+"\n"+
         "ET"+"\n"+
         "endstream"+"\n"+
         "endobj"+"\n", " 00000 n"],
         ["5 0 obj"+"\n"+
         "<</Font 8 0 R /ProcSet [/PDF /Text]>>"+"\n"+
         "endobj"+"\n", " 00000 n"],
         ["6 0 obj"+"\n"+
         "<</Type /Encoding /BaseEncoding /WinAnsiEncoding>>"+"\n"+
         "endobj"+"\n"," 00000 n"],
         ["7 0 obj"+"\n"+
         "<</Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding 6 0
R>>"+"\n"+
         "endobj"+"\n"," 00000 n"],
...

      private var ba:ByteArray = new ByteArray();
      public var xdpDocument:XdpDocument;

      public function PDFHelper(xdpDocument:XdpDocument) {
         this.xdpDocument = xdpDocument;
      }

      public function get pdfContent():ByteArray{
          return ba;
       }

      public function createPDFPrologue():void{
         //write pdf document prefix
         var xref:String ="";

         for (var i:int = 0; i < prefix.length; i++) {
            ba.writeMultiByte( prefix[i][0], "iso-8859-1");
            var str:String = padZeroes(ba.length, 10);
            xref = xref.concat(str + prefix[i][1] + " \n");
         }

         var s:String = xdpDocument.xdpContent.toString();
         s = s.substr(0, s.lastIndexOf("</subform>"));
         ba.writeMultiByte( s, "iso-8859-1");
       }
      public function createPage(obj:Object, type:int):void{
         var page:XdpPage = new XdpPage();
         createXdpPage(page, obj, type);
         ba.writeMultiByte(String(page.xdpContent), "iso-8859-1");
      }

     public function createPDFEpilogue():void{
         var xx:XML = xdpDocument.xdpContent as XML;
         ba.writeMultiByte( "</subform>"+"\r"+"</template>"+"\r", "iso-8859-1");
         ba.writeMultiByte( xx..ns_xci10::config[0].toString().replace("
xmlns:xdp=\"http://ns.adobe.com/xdp/\"", "")+"\r", "iso-8859-1");
         ba.writeMultiByte( xx..ns_xfals21::localeSet[0].toString().replace("
xmlns:xdp=\"http://ns.adobe.com/xdp/\"", "")+"\r", "iso-8859-1");
         ba.writeMultiByte( "</xdp:xdp>"+"\r", "iso-8859-1");
         ba.writeMultiByte( "endstream"+"\r", "iso-8859-1");
         ba.writeMultiByte( "endobj"+"\r", "iso-8859-1");
         ba.writeMultiByte("18 o obj " +"\n" + /*streamLength + */"\n" +
"endobj"+"\n",  "iso-8859-1");

         //the footer for the pdf document
          var end:String = "xref"+"\n"+ "0 " + 19 +"\n";
         var closing:String = end +
               "0000000000 65535 f"+"\r"+
               "0000000016 00000 n"+"\r"+
...
              "trailer"+"\n"+
               "<</Root 1 0 R  /Size " + 19 +">>"+"\n"+
               "startxref"+"\n"+
               "%%EOF"+"\n";
         ba.writeMultiByte(closing , "iso-8859-1");
       }

      public static function createXdpPage(root:XdpPage, obj:Object,
                type:int = 1):void{

         obj = resolveXdp(obj);
         if (obj is Container){

            var c:Container=obj as Container;
            var count:int=c.numChildren;
            if (type == TYPE_LIST) {
               var page:XdpPage = new XdpPage();
            } else {
               page = new XdpContainer();
            }

            page.x = new PNumber(c.x, PNumber.UNIT_PX);
            page.y = new PNumber(c.y, PNumber.UNIT_PX);
            page.h = new PNumber(c.height, PNumber.UNIT_PX);
            page.w = new PNumber(c.width, PNumber.UNIT_PX);

            root.addChild(page);

            if (obj is FormItem){
               var formItemLabel:Label = (obj as FormItem).itemLabel;
               createXdpPage(page, formItemLabel);
            }

            for(var i:int=0; i < count; i++){

               createXdpPage(page, c.getChildAt(i));
            }
         } else if (obj is IXdpObject){

            root.addChild(obj as IXdpObject);
         } else if (obj is UIComponent){

            var uiComp:UIComponent = obj as UIComponent;
            var xdp:XdpBaseObject = XdpImage.grab(uiComp);
            xdp.x = new PNumber(uiComp.x, PNumber.UNIT_PX);
            xdp.y = new PNumber(uiComp.y, PNumber.UNIT_PX);

            // set the width and hight of UIComponent (i.e. image)
            // for proper image scaling and conversion of pixels
            xdp.w = new PNumber(uiComp.width, PNumber.UNIT_PX);
            xdp.h = new PNumber(uiComp.height, PNumber.UNIT_PX);
            root.addChild(xdp);
         }
      }

}

Note

The code in Examples 11.16 and 11.17 is for illustration purposes only, because detailed coverage of the XDP generation is out of the scope of this book. Complete source code of com.farata.printing.swc, however, is available in the CVS repository of the Clear Toolkit project at SourceForge.

Example 11.18, “AIR application test4_xdp.mxml” shows the source code of test4_xdp.mxml, the modified version of text4.mxml, but this code generates a PDF in XDP format. The Flex window and PDF look the same as in Figure 11.3, “Running test4.mxml (left) generates PDF hw4 (right)”.

Example 11.18. AIR application test4_xdp.mxml

<?xml version="1.0" encoding="utf-8"?>

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="onCreationComplete(event)">

   <mx:Style source="main.css"/>

   <mx:DataGrid id="dg" x="0" y="0" width="100%" height="100%">
       <mx:columns>
      <mx:DataGridColumn dataField="name" headerText="Name"/>
      <mx:DataGridColumn dataField="phone" headerText="Phone"/>
      <mx:DataGridColumn dataField="email" headerText="E-mail"/>
      </mx:columns>

        </mx:DataGrid>
   <mx:ApplicationControlBar width="100%">
     <mx:Label text="File name:"/>
     <mx:TextInput id="txtFileName" text="hw4.pdf"/>
     <mx:Button label="Save PDF" click="savePdf()"/>
   </mx:ApplicationControlBar>

   <mx:Script>
      <![CDATA[
      import com.farata.printing.PrintOptions;
      import com.farata.printing.pdf.xdp.XdpDocument;
      import com.farata.printing.pdf.buffered.PDFHelper;

      private function doSavePdf():void{
         saveToFile(txtFileName.text , createXdpContent());
      }
      private function createXdpContent():ByteArray {

         var xdpDocument:XdpDocument=new XdpDocument();
         xdpDocument.init(new PrintOptions());
         var pdf:PDFHelper=new PDFHelper(xdpDocument);

         pdf.createPDFPrologue();
         pdf.createPage(canvas, PDFHelper.TYPE_LIST);
         pdf.createPDFEpilogue();

         return  pdf.pdfContent;
      }

      private function saveToFile(file:String, ba:ByteArray):void{

         var fs:FileStream=new FileStream();
         var f:File=File.desktopDirectory.resolvePath(file);

         fs.open(f, FileMode.WRITE);
         try{
            fs.writeBytes(ba);
         }catch(e:*){
                     // Error processing goes here
         }

         fs.close();
      }

      private function onCreationComplete(evt:*):void{

      var array:Array=[];
      for(var i:int=1; i <= 300; i++){

         var obj:Object=new Object();
         obj.name="User " + i;
         obj.phone="555-219-227" + i;
           obj.email="user" + i + "@hello.world";
           obj.active=(i % 2) == 1;

         array.push(obj);
           }

      dg.dataProvider=arrat;
     }

   ]]>
   </mx:Script>
</mx:WindowedApplication>

Note

We decided to keep the name of the code sample as test4_xdp.mxml, and you can find all other samples (test1 to test5) in the Flash Builder project clientPdfAir.

The previous example illustrates the printing of 300 rows of data to demonstrate that the pagination works properly and each page in the PDF file shows the header of the DataGrid (Figure 11.4, “The second page of the generated PDF hw5.pdf”).

Figure 11.4. The second page of the generated PDF hw5.pdf

The second page of the generated PDF hw5.pdf

Adding Printing to the PharmaSales Application

In Chapter 9, Working with Adobe AIR, you learned how to create an occasionally connected AIR application. In this section, you’ll modify it a little bit, armed with new knowledge and printing components. That’s right, the Acme Pharm dispatcher should be able to print visitation data for all salespeople.

On the other hand, each salesperson will be able to print the data about his visits to medical offices without the need to be connected to the server.

Printing for Acme Pharm Dispatchers

You’ll take care of the dispatcher’s needs first. As you might remember from Chapter 9, Working with Adobe AIR, VisitSchedules.mxml is a web application written in Flex. This means that you won’t be able to save a generated PDF file on the client’s filesystem and will need to send it to the server, which will just bounce it back so that the web browser will recognize it as a PDF and do the rest.

The source code of this version of PharmaSales is located in two Flex Builder projects, air.offline.demo.print and air.offline.demo.web.print. You’ll need to start with the latter (don’t forget to start MySQL Server and the servlet container first; the example uses Apache Tomcat). Your web browser should show you the view, similar to that shown in Figure 11.5, “Running VisitSchedules.mxml”.

Figure 11.5. Running VisitSchedules.mxml

Running VisitSchedules.mxml

Click the Print button and Figure 11.6, “Generated PDF from VisitSchedules.mxml”’s PDF will show up.

Figure 11.6. Generated PDF from VisitSchedules.mxml

Generated PDF from VisitSchedules.mxml

The PDF has been generated, and for illustration purposes, we performed a search for the word “Sandy,” which was successfully found.

The web browser reports that the PDF came from the following URL:

http://localhost:8080/air.offline.demo.web.print/PDFServlet/dg.pdf

You’ll see the code of this Java servlet, PDFServlet, in a little bit, but in the meantime, take a peek at the Flex code fragment in VisitSchedules.mxml (Example 11.19, “Flex code fragment for PDF generation”), which is invoked by clicking the Print button.

Example 11.19. Flex code fragment for PDF generation

<mx:Button click="openPDF(dg)" label="Print"/>
[Bindable]


   public var collection:DataCollection;

   private function openPDF(uiObject:Object):void{

      var xdpDocument:XdpDocument=new XdpDocument();
      var options:PrintOptions=new PrintOptions();
      options.paperSize=PaperSize.A4;
      options.orientation=PrintOptions.ORIENTATION_LANDSCAPE;

      xdpDocument.init(options);
      var pdf:PDFHelper=new PDFHelper(xdpDocument);

      pdf.createPDFPrologue();
      pdf.createPage(uiObject, PDFHelper.TYPE_LIST);
      pdf.createPDFEpilogue();

      sendToServer(uiObject.id + ".pdf", pdf.pdfContent);
   }


   private function sendToServer(file:String, ba:ByteArray):void{

      var req:URLRequest = new URLRequest("PDFServlet/"+file);
      req.method = URLRequestMethod.POST;

      ba.compress();

      req.data = ba;

      navigateToURL(req, "_blank");
   }

The function openPDF() looks similar to savePdf() from Example 11.18, “AIR application test4_xdp.mxml”. It’ll generate a PDF in the XDP format for the DataGrid container. At this point, the generated PDF is located in memory in the ByteArray object in pdf.pdfContent.

Next, the function sendToServer() compresses this ByteArray and sends it to the Java servlet,PDFServlet, deployed on the server. The source code of PDFServlet (Example 11.20, “PDFServlet.java”) is located in the folder src/com/Farata/demo/pdf in the Flex Builder project air.offline.demo.web.print.

Example 11.20. PDFServlet.java

package com.farata.demo.pdf;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.zip.InflaterInputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class PDFServlet extends javax.servlet.http.HttpServlet
                              implements javax.servlet.Servlet {

   static final long serialVersionUID = 1L;

   // The size of the reading block
   private static final int READ_BLOCK = 8192;

    public PDFServlet() {
      super();
   }

   protected void doPost(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {

      ServletInputStream in = req.getInputStream();
      InflaterInputStream iin = new InflaterInputStream(in);

      byte[] content = readAllFromInputStream(iin);

      resp.setContentType("application/pdf");
      resp.flushBuffer();

      resp.getOutputStream().write(content);
      resp.getOutputStream().close();
   }

   private byte[] readAllFromInputStream(InputStream is) throws IOException {

      // create channel for input stream
      ReadableByteChannel bc = Channels.newChannel(is);

      ByteBuffer bb = ByteBuffer.allocate(READ_BLOCK);

      while (bc.read(bb) != -1) {
         // get new buffer for read
         bb = resizeBuffer(bb);
      }

      bb.flip();

      return bb.array();
   }

   private ByteBuffer resizeBuffer(ByteBuffer in) {
      ByteBuffer result = in;

      if (in.remaining() < READ_BLOCK) {
         // create new buffer
         result = ByteBuffer.allocate(in.capacity() * 2);

         // set limit to current position in buffer and set position to zero.
         in.flip();

         // store the content of original buffer to new buffer
         result.put(in);
      }

      return result;

   }
}

In short, this Java servlet echoes received PDF content from the client, assigns to it the MIME type "application/pdf", and sends it right back without doing any other processing.

Start reading this code from the method doPost(), which opens an input stream pointing at the request object (HTTPRequest) that arrived from the browser. Because the arrived content has been compressed by the client, the servlet inflates it first and writes it right back to the response object (HTTPResponse).

All manipulations with buffering in the code above are done for I/O efficiency.

The main takeaway here is that the server-side code didn’t modify the received PDF object, but just sent it back as PDF content. Now it’s the web browser’s responsibility to engage its Acrobat Reader plug-in to display the document.

Printing for Acme Pharm Salespeople

Now consider the AIR application that salespeople use on a daily basis, either in connected or in disconnected mode. In this case, the generated PDF won’t even go to the server side, but it will be saved in the file on the local disk.

You still want to print the list of visits for a particular salesperson as a grid, but to make this report a little fancier, the program should add the name of the salesperson as a header and the logo of Acme Pharm in the footer’s area of the report.

After running the application PharmaSales.mxml from the air.offline.demo.print project filtering the data for visits done by Liz Anthony from February 5 to June 7 in 2009, click the Print button. The filtered data will be saved in the file dg.pdf at the local storage directory. Exact file location is displayed in the status bar, as shown in Figure 11.7, “After clicking the buttons Filter and Print”.

After the PDF file is created, this AIR application automatically starts the web browser and opens dg.pdf. Figure 11.8, “A generated local file with header and footer: dg.pdf” shows how it looks for the sample data.

The header of dg.pdf shows the date range and the footer—the logo of the company and some arbitrary text. The header and the footer will be repeated on each page in case of multipage printing.

Figure 11.7. After clicking the buttons Filter and Print

After clicking the buttons Filter and Print

The header component looks like Example 11.21, “Header.mxml”.

Example 11.21. Header.mxml

<?xml version="1.0" encoding="utf-8"?>

 <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="594" height="56">

  <mx:Label id="headerLabel" x="10" y="13" text="Visits by Liz Anthony for the
   period from 02/05/09 to 06/07/09" width="565" height="27" fontSize="16"
   fontFamily="Arial" fontWeight="bold"/>

 </mx:Canvas>

Figure 11.8. A generated local file with header and footer: dg.pdf

A generated local file with header and footer: dg.pdf

The footer components are shown in Example 11.22, “Footer.mxml”.

Example 11.22. Footer.mxml

<?xml version="1.0" encoding="utf-8"?>

<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="556" height="78"
   xmlns:ns1="com.farata.controls.*">

 <ns1:Image source="@Embed(source='assets/pharma_small.jpg')" x="10" y="10"
  width="75" height="57"/>

 <ns1:Label x="100" y="27" width="446" height="34" text="Acme Pharma - your
  reliable source of drugs" fontSize="19"/>

</mx:Canvas>

Example 11.23, “Code fragment for printing a data grid with visits from PharmaSales.mxml” is a code fragment from the PharmaSales.mxml application. It illustrates what’s happening when the user clicks on the Print button.

Example 11.23. Code fragment for printing a data grid with visits from PharmaSales.mxml

<mx:Canvas height="100%" width="100%">
   <mx:Panel title="Pharma Sales - Salesman" width="100%" height="100%">
     <mx:ControlBar>
      <mx:Label text="Select Date Range:"/>
         <mx:DateField id="dateRangeFrom"
                 enabled="{!showAll.selected}"/>
       <mx:DateField id="dateRangeTo"
                 enabled="{!showAll.selected}"/>
       <mx:CheckBox id="showAll" label="Show all"/>
       <mx:Button label="Filter" click="doFilter()"/>
   </mx:ControlBar>

   <fx:DataGrid toolTip="Double click for details" doubleClick="onDoubleClick()"
       doubleClickEnabled="true" horizontalScrollPolicy="auto" width="100%"
       id="dg" dataProvider="{visitDataCollection}" editable="true" height="100%">
...

   <mx:Button click="openVisitDataCollectionPDF(dg)" label="Print"/>

</mx:Canvas>

   <mx:Script>
      <![CDATA[

      import com.farata.printing.PaperSize;
      import com.farata.printing.PrintOptions;
      import com.farata.printing.pdf.xdp.XdpDocument;
      import com.farata.printing.pdf.buffered.PDFHelper;

   private function openVisitDataCollectionPDF(uiObject:Object):void{

            var xdpDocument:XdpDocument=new XdpDocument();
            var options:PrintOptions=new PrintOptions();
            options.paperSize=PaperSize.A4;
            options.orientation=PrintOptions.ORIENTATION_LANDSCAPE;
            xdpDocument.init(options);

            //Create header text dynamically

      var text:String="";

      var df:DateFormatter=new DateFormatter();
      if (showAll.selected || (!dateRangeFrom.selectedDate &&
                                 !dateRangeTo.selectedDate)){
         text="All visits by " + username.text;
      } else if (!dateRangeFrom.selectedDate && dateRangeTo.selectedDate){
         text="Visits by " + username.text + " to " +
                   df.format(dateRangeTo.selectedDate);
      } else if (dateRangeFrom.selectedDate && !dateRangeTo.selectedDate){
             text="Visits by " + username.text + " from " +
                   df.format(dateRangeFrom.selectedDate);
      } else {
           text="Visits by " + username.text + " from " +
                   df.format(dateRangeFrom.selectedDate) + " to " +
                   df.format(dateRangeTo.selectedDate);
         }

        var header:Header=new Header();
      header.initialize();
        header.headerLabel.text=text;

      xdpDocument.header=header;
      xdpDocument.footer=new Footer();
      xdpDocument.footer.initialize();

      var pdf:PDFHelper=new PDFHelper(xdpDocument);

      pdf.createPDFPrologue();

      pdf.createPage(uiObject, PDFHelper.TYPE_LIST);

      pdf.createPDFEpilogue();

      savePDF(uiObject.id + ".pdf", pdf.pdfContent);
   }

   private function savePDF(file:String, ba:ByteArray):void {

      var fs:FileStream=new FileStream();
      var f:File=File.applicationStorageDirectory.resolvePath(file);

        try{
        fs.open(f, FileMode.WRITE);
        fs.writeBytes(ba);

           var req:URLRequest=new URLRequest(f.url);
        navigateToURL(req, "_blank");
        status="Saved to " + f.nativePath;

      } catch(e:*){
         status=e.message;
        } finally {
         fs.close();
       }

   }
   ]]>

   </mx:Script>

</mx:WindowedApplication>

The Visit Details window now has the Print button, too, as you can see in Figure 11.9, “Visit Details with the Print button”.

The produced PDF file looks like Figure 11.10, “The generated PDF file details_panel.pdf”.

Figure 11.9. Visit Details with the Print button

Visit Details with the Print button

Figure 11.10. The generated PDF file details_panel.pdf

The generated PDF file details_panel.pdf

There is no reason why a Flex window with Google Maps can’t have the Print button, as shown in Figure 11.11, “A Google Maps window with the Print button”.

Figure 11.11. A Google Maps window with the Print button

A Google Maps window with the Print button

The generated PDF with the map is shown in Figure 11.12, “A Google map in a generated PDF”.

The PDF files for both the Visit Details and map windows are generated by similar functions, as shown in Example 11.24, “Functions for generation of Visit Details and map PDFs”.

Example 11.24. Functions for generation of Visit Details and map PDFs

private function openVisitPDF(uiObject:Object):void{

   var xdpDocument:XdpDocument=new XdpDocument();

     var options:PrintOptions=new PrintOptions();
   options.paperSize=PaperSize.A4;
   options.orientation=PrintOptions.ORIENTATION_LANDSCAPE;

   xdpDocument.init(options);
   xdpDocument.footer=new Footer();
   xdpDocument.footer.initialize();

   var pdf:PDFHelper=new PDFHelper(xdpDocument);
   pdf.createPDFPrologue();
   pdf.createPage(uiObject, PDFHelper.TYPE_PAGE);
   pdf.createPDFEpilogue();

   savePDF(uiObject.id + ".pdf", pdf.pdfContent);
}


private function openGoogleMapPDF(uiObject:Object):void{

   var xdpDocument:XdpDocument=new XdpDocument();

   var options:PrintOptions=new PrintOptions();
   options.paperSize=PaperSize.A4;
   options.orientation=PrintOptions.ORIENTATION_LANDSCAPE;

   xdpDocument.init(options);
   xdpDocument.footer=new Footer();
   xdpDocument.footer.initialize();

   var pdf:PDFHelper=new PDFHelper(xdpDocument);

   pdf.createPDFPrologue();
   pdf.createPage(uiObject, PDFHelper.TYPE_PAGE);
   pdf.createPDFEpilogue();

   savePDF(uiObject.id + ".pdf", pdf.pdfContent);
}

Figure 11.12. A Google map in a generated PDF

A Google map in a generated PDF

ClearBI: A Web Reporter for Flex

If you want to make more professional-looking reports with such features as adding formulas, creating totals and subtotals, exporting to Microsoft Excel, and charting, consider using the ClearBI reporter that will be included in a future version of the Clear Toolkit framework. To run these reports, end users don’t need anything but Flash Player–enabled web browsers.

Flex developers use ClearBI’s Designer (an AIR application) to create custom reports that can either be saved on the local drives or published in a DBMS.

More advanced business users can customize their reports right inside the web browser. For example, Figure 11.13, “A sample report with grouping” depicts a report with grouping by state, and departments with calculated totals and subtotals.

Figure 11.13. A sample report with grouping

A sample report with grouping

When the user directs a web browser to a deployed ClearBI report player application (a SWF file; it’ll arrive with an extra toolbar—see the toolbar below the address bar in Figure 11.13, “A sample report with grouping”) that allows users to zoom in, export to Microsoft Excel, generate PDFs, and open a Designer view that allows you to create charts, grouping, filters, sorting, and format masks; compute fields; and introduce formulas. Figure 11.14, “ClearBI Designer” depicts a Designer view with a formula, converting department codes into titles.

ClearBI Designer can be invoked either by the user inside the web browser, or by any junior developer as a standalone AIR application (no knowledge of Flex is required).

ClearBI supports user roles and hierarchies to specify who can access specific reports.

Figure 11.14. ClearBI Designer

ClearBI Designer

Summary

In this chapter, you’ve learned how to extend Flex components to give them PDF generation capabilities. We encourage you to experiment in this area so you can be in full control of your own set of printable controls that reflect all printing needs of your enterprise. On the other hand, we offer you our version of such components, which are included in Clear Toolkit components—ready to use.

The principle of PDF generation on the client described in this chapter has several advantages:

  • You don’t have to create separate templates for further merging with data—everything is happening at the component level.

  • Pagination of the long documents is also taken care of by the client-side code.

  • Produced documents are fully searchable.

  • If you sent editable components from Flex (e.g., a DataGrid), they will remain editable in the PDF document, too.

All source code for the examples used in this chapter is located under the Flex Builder projects air.offline.demo.print, air.offline.demo.web.print, PrintingSamples, and clientPdfAir. We’ve also included for your convenience the source code of the package com.farata.printing, which is a part of clear.swc. But to get the up-to-date version of the code of all components included in this library, visit the SourceForge repository of the Clear Toolkit framework at https://sourceforge.net/projects/cleartoolkit/.

Wouldn’t you agree that with our smart Flex components, the task of printing becomes almost trivial?

If you enjoyed this excerpt, buy a copy of Enterprise Development with Flex.

 

Posted by 1010
반응형

Printing Flex Data Grids

One of the questions that I was asked at Colorado Software Summit 2008 after one of my three presentations of Applying Flash to Java: Flex and OpenLaszlo was if there is a way to print the contents of a DataGrid. In this blog entry, I demonstrate simple application of the Flex PrintDataGrid control for doing just that.

The PrintDataGrid control component and the closely related PrintAdvancedDataGrid control component are components meant for visualization only. While they each inherit properties and behaviors from their respective parent classes (DataGrid and AdvancedDataGrid), the print-specific classes should not use any functionality that is associated with user interaction. Note also that the print-specific child classes are in the mx.printing package while their parents are in the mx.controls package.

One of the particularly useful aspects of the Flex 3 Language Reference is that it often includes example code for the language items being described. When the Flex item being described is visual, this reference often includes an actual running instantiation of the example code via the Flash Player. Fortunately, this is true of the PrintDataGrid, which includes multiple examples and does include an actual running Flash-based rendering of the examples.

I am basing my example of using the PrintDataGrid on the DataGrid I previously used in the blog entry Communication Between Two Flex Applications. Here, on one file called SimplePrintDataGridExample.mxml, is the entire code listing demonstrating a basic use of PrintDataGrid.

SimplePrintDataGridExample.mxml

  1. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"  
  2.                 viewSourceURL="http://localhost:8080/Css2008FlashExamples/flex/SimpleDataGrid/SimplePrintDataGridExample.mxml">  
  3.   
  4.    <mx:Script>  
  5.    <![CDATA[ 
  6.    import mx.controls.Alert; 
  7.    import flash.printing.PrintJobOrientation; 
  8.    import mx.printing.FlexPrintJob; 
  9.    import mx.printing.FlexPrintJobScaleType; 
  10.    import mx.printing.PrintDataGrid; 
  11.  
  12.    /** 
  13.     * Prints contents of DataGrid in print-friendly format. 
  14.     * 
  15.     * <p>ADDITIONAL REFERENCES:</p> 
  16.     * http://livedocs.adobe.com/flex/3/langref/mx/printing/FlexPrintJob.html 
  17.  
  18.     * http://livedocs.adobe.com/flex/3/html/printing_3.html 
  19.     */ 
  20.    public function printDataGridContents():void 
  21.    { 
  22.       const printJob:FlexPrintJob = new FlexPrintJob(); 
  23.  
  24.       if ( printJob.start() ) 
  25.       { 
  26.          const printDataGrid:PrintDataGrid = new PrintDataGrid(); 
  27.          printDataGrid.width = printJob.pageWidth; 
  28.          printDataGrid.height = printJob.pageHeight; 
  29.          printDataGrid.columns = dataGrid.columns; 
  30.          printDataGrid.dataProvider = presenters.copy(); 
  31.          printDataGrid.visible = false; 
  32.          Application.application.addChild(printDataGrid); 
  33.          while (printDataGrid.validNextPage) 
  34.          { 
  35.             printDataGrid.nextPage(); 
  36.             printJob.addObject(printDataGrid); 
  37.          } 
  38.          printJob.send(); 
  39.          Application.application.removeChild(printDataGrid); 
  40.       } 
  41.    } 
  42.    ]]>  
  43.    </mx:Script>  
  44.   
  45.    <mx:XMLList id="presenters">  
  46.      <presenter>  
  47.        <lastname>Johnsson</lastname>  
  48.        <firstname>Dan Bergh</firstname>  
  49.        <organization>OmegaPoint AB</organization>  
  50.        <url>http://softwaresummit.org/2008/speakers/johnsson.htm</url>  
  51.      </presenter>  
  52.      <presenter>  
  53.        <lastname>Kaplan-Moss</lastname>  
  54.        <firstname>Jacob</firstname>  
  55.        <organization>Whiskey Media</organization>  
  56.        <url>http://softwaresummit.org/2008/speakers/kaplan-moss.htm</url>  
  57.      </presenter>  
  58.      <presenter>  
  59.        <lastname>Lan</lastname>  
  60.        <firstname>Ikai</firstname>  
  61.        <organization>LinkedIn</organization>  
  62.        <url>http://softwaresummit.org/2008/speakers/lan.htm</url>  
  63.      </presenter>  
  64.      <presenter>  
  65.        <lastname>Marx</lastname>  
  66.        <firstname>Dustin</firstname>  
  67.        <organization>Raytheon Company</organization>  
  68.        <url>http://softwaresummit.org/2008/speakers/marx.htm</url>  
  69.      </presenter>  
  70.      <presenter>  
  71.        <lastname>Morrill</lastname>  
  72.        <firstname>Dan</firstname>  
  73.        <organization>Google</organization>  
  74.        <url>http://softwaresummit.org/2008/speakers/morrill.htm</url>  
  75.      </presenter>  
  76.      <presenter>  
  77.        <lastname>Moskowitz</lastname>  
  78.        <firstname>David</firstname>  
  79.        <organization>Productivity Solutions, Inc.</organization>  
  80.        <url>http://softwaresummit.org/2008/speakers/moskowitz.htm</url>  
  81.      </presenter>  
  82.      <presenter>  
  83.        <lastname>Opstvedt</lastname>  
  84.        <firstname>Hermod</firstname>  
  85.        <organization>DnB NOR</organization>  
  86.        <url>http://softwaresummit.org/2008/speakers/opstvedt.htm</url>  
  87.      </presenter>  
  88.    </mx:XMLList>  
  89.   
  90.    <mx:Panel id="mainPanel" width="75%"  
  91.              title="CSS 2008 DataGrid Example">  
  92.       <mx:DataGrid id="dataGrid"  
  93.                    width="100%" height="100%"  
  94.                    rowCount="5" dataProvider="{presenters}">  
  95.          <mx:columns>  
  96.             <mx:DataGridColumn dataField="lastname"  
  97.                                headerText="Last Name"  
  98.                                width="75" />  
  99.             <mx:DataGridColumn dataField="firstname"  
  100.                                headerText="First Name"  
  101.                                width="75" />  
  102.             <mx:DataGridColumn dataField="organization"  
  103.                                headerText="Organization"  
  104.                                width="100" />  
  105.             <mx:DataGridColumn dataField="url"  
  106.                                headerText="CSS URL"  
  107.                                width="200" />  
  108.          </mx:columns>  
  109.       </mx:DataGrid>  
  110.       <mx:Button click="printDataGridContents();" label="Print!" />  
  111.    </mx:Panel>  
  112.   
  113. </mx:Application>  


The original DataGrid (the one intended for interaction and not for printing that is defined in MXML rather than in ActionScript) is essentially the same as the one I used in the Flex-to-Flex Communication blog entry. A button has been added in this example to kick off the ActionScript method printDataGridContents().

The ActionScript method that is invoked when the button is presssed only directly associate the original, interactive DataGrid with the PrintDataGrid to get column names for the PrintDataGrid. For the source data, the PrintDataGrid is associated (via dataProvider property) with the same XML data that backs the original, interactive grid. Because I don't want the PrintDataGrid affecting the original XML, I used the XMLList.copy() function.

This example also demonstrates how the width and height of the PrintDataGrid can be associated with the FlexPrintJob's width and height.

The following two screen snapshots demonstrate how the Flex application whose code is listed above appears when it is first started and when the Print! button is clicked.


Initial Appearance of SimplePrintDataGridExample



Print! Option Selected via Button Click



When a printer is selected, the print-out of this data looks like that shown in the next screen snapshot.


Printed Output



The Google Chrome web browser was used for all of the above screen snapshots. One of the positive features of Google Chrome is its Developer's "View source" capability. The regular "View Source" capability shows the MXML source code without any translation of the original text and is not very appealing. However, one can choose Develooer->View source on that unappealing display of source to see source with color coding and line numbers as shown in the next screen snapshot.


Google Chrome Display of Developer->View source




I had explicitly made source code available for this Flex application (a process I described in a previous blog entry) so it is nice to have that source code displayed with color coding and line numbers.


Printing from Flex/Flash is Not Without Challenges

In my example here, I depended on the user (me!) to choose to print out in "landscape" mode. Had I printed in "portrait" mode, not all grid information would have been printed. Jesse Warden has a blog entry Forcing Landscape Printing in Flex 2 and 3 that talks about some of the challenges associated with trying to print in landscape mode from Flex.

Even though FlexPrintJob is easy to use, printing from Flex (or more specifically from Flash) can still be tricky, especially when there are multiple pages involved. Some efforts to make this easier include FlexReport and Lin Lin: Tips for using PrintingJob in flex 1.5. Of course, printing has been a difficult issue for many fine programming languages.


Conclusion

The FlexPrintJob object and associated components intended specifically for printing make it possible with relatively few lines of code to print out Flex data. I have found that it does require some trial-and-error to get the printing where I want it. There are also issues of how the user attempts to print (using the web browers's Print icon or option rather than the Flex application provided print capability), but these can be at least partially handled via training and/or use of JavaScript to disable those browser print features. Also, another nice option is to mix Flex with DHTML and have the print support be implemented with a static HTML page created specifically for printing.
Posted by 1010
반응형

PrintDataGrid,percentHeight and multipage layouts

Categories: actionscript, flex
Written By: sebi

I rarely had to create any complex layouts for printing with Flex, until now.
The layout has to have different pages, with different elements on them. The issue is that the PrintDataGrid calculated it’s height right (well, almost) at the first page, but maintained this value to the rest of the pages – instead of recalculating based on that structures.

There is a chapter in the Flex help discussing this scenario: Using the PrintDataGrid control for multipage grids.

I implemented the whole print layout view based on this description, and quickly realized that even the result is not exactly what I wanted.

Print result

Print result

The help suggest something that sounds relevant to this issue, but it didn’t solve my problem.

When you use a PrintDataGrid control to print a single data grid across multiple pages, you queue each page of the grid individually. If your application customizes each page beyond simply using the nextPage() method to page through the PrintDataGrid, you must call the validateNow() method to update the page layout before you print each page, as shown in Print output component.

The root of the problem is that the PrintDataGrid’s setActualSize contains an interesting line, which resets the percentHeight of the grid to NaN. As a result, the parent container of the grid won’t resize the grid, even if it’s layout changes, and we call validateNow().

	/**
	 *  @private
	 *  setActualSize() is overridden to update _originalHeight.
	 */
	override public function setActualSize(w:Number, h:Number):void
	{
		if (!isNaN(percentHeight))
		{
			_originalHeight = h;
			super.percentHeight = NaN;
			measure();
			h = measuredHeight;
		}
 
		super.setActualSize(w, h);
 
		invalidateDisplayList();
 
		if (sizeToPage && !isNaN(explicitHeight))
			explicitHeight = NaN;
	}

The whole resizing cycle of the grid seemed to be quite complex, so I decided to look for an easier solution than trying to extend-override some of it’s steps, so ended up with the following:

	public function showPage( pageType:String ):void 
	{
		switch( pageType )
		{
			case "first":
			case "single":
				meetingInfoExtended.visible 		= true;
				meetingInfoExtended.includeInLayout	= true;
				meetingInfo.visible 		= false;
				meetingInfo.includeInLayout= false;						
				break;
			case "middle":
			case "last":
				meetingInfoExtended.visible 		= false;
				meetingInfoExtended.includeInLayout	= false;
				meetingInfo.visible 		= true;
				meetingInfo.includeInLayout= true;						
				break; 
		}
		printingGrid.percentHeight = 100;
		validateNow();
	}

Basically re-apply the percentHeight setting in every paging, forcing the grid to recalculate it’s size.

There was another small glitch, what caused a scrollbar to appear in the print layout – at least it tried to appear. I have a multi-line text there, an mx:Text, and I think the text couldn’t calculate it height fast enough, but if I didn’t pass a fixed height to it, the grid miss-calculated it’s height. But only the text caused this problem with the percent heights, other components could handle it.

One Response to “PrintDataGrid,percentHeight and multipage layouts”

  1. Robert Cesaric Says:

    Thanks for post.. We were just building a print view that head custom header/footer and had the same issue. Your percentHeight fix worked perfectly.

    We’re still noticing issues priting with the datagrid variableRowHeight but I’m much happier getting this bug fixed first. Thx!

Posted by 1010
반응형

RichTextEditor control

The RichTextEditor control lets users enter, edit, and format text. Users apply text formatting and URL links by using subcontrols that are located at the bottom of the RichTextEditor control.

For complete reference information, see the Adobe Flex Language Reference.

About the RichTextEditor control

The RichTextEditor control consists of a Panel control with two direct children:

  • A TextArea control in which users can enter text
  • A tool bar container with format controls that let a user specify the text characteristics. Users can use the tool bar subcontrols to apply the following text characteristics:
    • Font family
    • Font size

      Note: When using the RichTextEditor control, you specify the actual pixel value for the font size. This size is not equivalent to the relative font sizes specified in HTML by using the size attribute of the HTML <font> tag.

       

    • Any combination of bold, italic and underline font styles
    • Text color
    • Text alignment: left, center, right, or justified
    • Bullets
    • URL links

The following image shows a RichTextEditor control with some formatted text:

RichTextEditor control with some formatted text

For the source for this example, see Creating a RichTextEditor control.

You use the RichTextEditor interactively as follows:

  • Text that you type is formatted as specified by the control settings.
  • To apply new formatting to existing text, select the text and set the controls to the required format.
  • To create a link, select a range of text, enter the link target in the text box on the right, and press Enter. You can only specify the URL; the link always opens in a _blank target. Also, creating the link does not change the appearance of the link text; you must separately apply any color and underlining.
  • You can cut, copy, and paste rich text within and between Flash HTML text fields, including the RichTextEditor control's TextArea subcontrol, by using the normal keyboard commands. You can copy and paste plain text between the TextArea and any other text application, such as your browser or a text editor.

Creating a RichTextEditor control

You define a RichTextEditor control in MXML by using the <mx:RichTextEditor> tag, as the following example shows. Specify an id value if you intend to refer to a control elsewhere in your MXML, either in another tag or in an ActionScript block.

<?xml version="1.0"?>
<!-- textcontrols/RichTextEditorControl.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:RichTextEditor id="myRTE" text="Congratulations, winner!" />
</mx:Application>

The executing SWF file for the previous example is shown below:

 

You can use the text property to specify an unformatted text string, or the htmlText property to specify an HTML-formatted string. For more information on using these properties, see Using the text property, and Using the htmlText property. For information on selecting, replacing, and formatting text that is in the control, see Selecting and modifying text.

The following example shows the code used to create the image in About the RichTextEditor control:

<?xml version="1.0"?>
<!-- textcontrols/RichTextEditorControlWithFormattedText.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <!-- The HTML text string used to populate the RichTextEditor control's
     TextArea subcontrol. The text is on a single line. -->
  <mx:Script><![CDATA[
     [Bindable]
     public var htmlData:String="<textformat leading='2'><p align='center'><b><font size='20'>HTML Formatted Text</font></b></p></textformat><br><textformat leading='2'><p align='left'><font face='_sans' size='12' color='#000000'>This paragraph contains<b>bold</b>, <i>italic</i>, <u>underlined</u>, and <b><i><u>bold italic underlined </u></i></b>text.</font></p></textformat><br><p><u><font face='arial' size='14' color='#ff0000'>This a red underlined 14-point arial font with no alignment set.</font></u></p><p align='right'><font face='verdana' size='12' color='#006666'><b>This a teal bold 12-pt.' Verdana font with alignment set to right.</b></font></p><br><li>This is bulleted text.</li><li><font face='arial' size='12' color='#0000ff'><u> <a href='http://www.adobe.com'>This is a bulleted link with underline and blue color set.</a></u></font></li>";
  ]]></mx:Script>

  <!-- The RichTextEditor control. To reference a subcontrol prefix its ID with the RichTextEditor control ID. -->
  <mx:RichTextEditor id="rte1" 
     backgroundColor="#ccffcc" 
     width="500"
     headerColors="[#88bb88, #bbeebb]" 
     footerColors="[#bbeebb, #88bb88]" 
     title="Rich Text Editor" 
     htmlText="{htmlData}" 
     initialize="rte1.textArea.setStyle('backgroundColor', '0xeeffee')"
  />
</mx:Application>

The executing SWF file for the previous example is shown below:

 

Sizing the RichTextEditor control

The control does not resize in response to the size of the text in the TextArea control. If the text exceeds the viewable space, by default, the TextArea control adds scroll bars. If you specify a value for either the height or width property but not both, the control uses the default value for the property that you do not set.

If you set a width value that results in a width less than 605 pixels wide, the RichTextEditor control stacks the subcontrols in rows.

Programming RichTextEditor subcontrols

Your application can control the settings of any of the RichTextEditor subcontrols, such as the TextArea, the ColorPicker, or any of the ComboBox or Button controls that control text formatting. To refer to a RichTextEditor subcontrol, prefix the requested control's ID with the RichTextEditor control ID. For example, to refer to the ColorPicker control in a RichTextEditor control that has the ID rte1, use rte1.colorPicker.

Inheritable styles that you apply directly to a RichTextEditor control affect the underlying Panel control and the subcontrols. Properties that you apply directly to a RichTextEditor control affect the underlying Panel control only.

For more information, see the RichTextEditor in the Adobe Flex Language Reference.

Setting RichTextEditor subcontrol properties and styles

The following simple code example shows how you can set and change the properties and styles of the RichTextEditor control and its subcontrols. This example uses styles that the RichTextEditor control inherits from the Panel class to set the colors of the Panel control header and the tool bar container, and sets the TextArea control's background color in the RichTextEditor control's creationComplete event member. When users click the buttons, their click event listeners change the TextArea control's background color and the selected color of the ColorPicker control.

<?xml version="1.0"?>
<!-- textcontrols/RTESubcontrol.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
     height="420">
  <!-- The RichTextEditor control. To set the a subcontrol's style or property, fully qualify the control ID. The footerColors style sets the ControlBar colors. -->
  <mx:RichTextEditor id="rte1" 
     backgroundColor="#ccffcc" 
     headerColors="[#88bb88, #bbeebb]" 
     footerColors="[#bbeebb, #88bb88]" 
     title="Rich Text Editor" 
     creationComplete="rte1.textArea.setStyle('backgroundColor','0xeeffee')" 
     text="Simple sample text"
  />

  <!-- Button to set a white TextArea background. -->
  <mx:Button 
     label="Change appearance" 
     click="rte1.textArea.setStyle('backgroundColor', '0xffffff');rte1.colorPicker.selectedIndex=27;"
  />

  <!-- Button to reset the display to its original appearance. --> 
  <mx:Button 
     label="Reset Appearance" 
     click="rte1.textArea.setStyle('backgroundColor', '0xeeffee');rte1.colorPicker.selectedIndex=0;"
  />
</mx:Application>

The executing SWF file for the previous example is shown below:

 

Removing and adding RichTextEditor subcontrols

You can remove any of the standard RichTextEditor subcontrols, such as the alignment buttons. You can also add your own subcontrols, such as a button that pops up a find-and-replace dialog box.

Remove an existing subcontrol

  1. Create a function that calls the removeChildAt method of the editor's tool bar Container subcontrol, specifying the control to remove.
  2. Call the method in the RichTextEditor control's initialize event listener.

The following example removes the alignment buttons from a RichTextEditor control, and shows the default appearance of a second RichTextEditor control:

<?xml version="1.0"?>
<!-- textcontrols/RTERemoveAlignButtons.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script><![CDATA[
        public function removeAlignButtons():void {
            rt1.toolbar.removeChild(rt1.alignButtons);
        }
    ]]></mx:Script>
  
    <mx:HBox>
        <mx:RichTextEditor id="rt1" 
            title="RichTextEditor With No Align Buttons" 
            creationComplete="removeAlignButtons()"
        />
        <mx:RichTextEditor id="rt2" 
            title="Default RichTextEditor"
        />
    </mx:HBox>
  
</mx:Application>

The executing SWF file for the previous example is shown below:

 

Add a new subcontrol

  1. Create an ActionScript function that defines the subcontrol. Also create any necessary methods to support the control's function.
  2. Call the method in the RichTextEditor control's initialize event listener, as in the following tag:
    <mx:RichTextEditor id="rt" initialize="addMyControl()"
    
    

The following example adds a find-and-replace dialog box to a RichTextEditor control. It consists of two files: the application, and a custom TitleWindow control that defines the find-and-replace dialog (which also performs the find-and-replace operation on the text). The application includes a function that adds a button to pop up the TitleWindow, as follows:

<?xml version="1.0"?>
<!-- textcontrols/CustomRTE.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            import mx.controls.*;
            import mx.containers.*;
            import flash.events.*;
            import mx.managers.PopUpManager;
            import mx.core.IFlexDisplayObject;

            /* The variable for the pop-up dialog box. */
            public var w:IFlexDisplayObject;

            /* Add the Find/Replace button to the Rich Text Editor control's
               tool bar container. */
            public function addFindReplaceButton():void {
                var but:Button = new Button();
                but.label = "Find/Replace";
                but.addEventListener("click",findReplaceDialog);
                rt.toolbar.addChild(but);
            }

            /* The event listener for the Find/Replace button's click event
               creates a pop-up with a MyTitleWindow custom control. */
            public function findReplaceDialog(event:Event):void {
                var w:MyTitleWindow = MyTitleWindow(
                    PopUpManager.createPopUp(this, MyTitleWindow, true)
                );
                w.height=200;
                w.width=340;

                /* Pass the a reference to the textArea subcontrol
                   so that the custom control can replace the text. */
                w.RTETextArea = rt.textArea;
                PopUpManager.centerPopUp(w);
            }
        ]]>
    </mx:Script>

    <mx:RichTextEditor id="rt" width="95%" 
        title="RichTextEditor"
        text="This is a short sentence."
        initialize="addFindReplaceButton();"
    />

</mx:Application>

The executing SWF file for the previous example is shown below:

 

The following MyTitleWindow.mxml file defines the custom myTitleWindow control that contains the find-and-replace interface and logic:

<?xml version="1.0"?>
<!-- A TitleWindow that displays the X close button. Clicking the close button 
only generates a CloseEvent event, so it must handle the event to close the control. -->
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" 
    title="Find/Replace" 
    showCloseButton="true" 
    close="closeDialog();"
>
    <mx:Script>
        <![CDATA[
            import mx.controls.TextArea;
            import mx.managers.PopUpManager;

            /* Reference to the RichTextArea textArea subcontrol.
               It is set by the application findReplaceDialog method
               and used in the replaceAndClose method, below. */
            public var RTETextArea:TextArea;

            /* The event handler for the Replace button's click event.
               Replace the text in the RichTextEditor TextArea and
               close the dialog box. */
            public function replaceAndClose():void{
                RTETextArea.text = RTETextArea.text.replace(ti1.text, ti2.text);
                PopUpManager.removePopUp(this);
            }

            /* The event handler for the TitleWindow close button. */
            public function closeDialog():void {
                PopUpManager.removePopUp(this);
            }

        ]]>
    </mx:Script>

    <!-- The TitleWindow subcontrols: the find and replace inputs, 
         their labels, and a button to initiate the operation. -->
    <mx:Label text="Find what:"/>
    <mx:TextInput id="ti1"/>
    
    <mx:Label text="Replace with:"/>
    <mx:TextInput id="ti2"/>
    
    <mx:Button label="Replace" click="replaceAndClose();"/> 
</mx:TitleWindow>

Posted by 1010
반응형

Simple TLF Text Editor released

I just released a simple TLF-based text editor. It allows the end user to have a familiar interface and generates TextFlow on the fly as the user types. It’s simple and light-weight and supports most commonly used styles. It’s lighter weight than Adobe’s tlf text editor. This editor is perfect to use in a CMS.

I even implemented what I’m calling “soft bullets”. TLF 1.x doesn’t officially support bullets so I implemented it myself. The reason it’s soft is because you can insert the cursor before the bullet and type text and there are some other strange quirks, but it’s good enough to give the end user some decent styling options.

The source is released under MIT license, so feel free to use it in your apps and modify it for your needs. You can get the code on Google code.

This entry was posted in Components, TLF. Bookmark the permalink.

 

Posted by 1010
반응형

Exporting a TextFlow object in Flex 4

By On July 25, 2009 · 28 Comments

The following example shows how you can export a TextFlow object in Flex 4 by using the TextConverter class (flashx.textLayout.conversion.TextConverter), and specifying HTML format, plain text format, or Text Layout Format.

Full code after the jump.

The following example(s) require Flash Player 10 and the Adobe Flex 4 SDK. To download the Adobe Flash Builder 4 trial, see http://www.adobe.com/products/flex/. To download the latest nightly build of the Flex 4 SDK, see http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4.
For more information on getting started with Flex 4 and Flash Builder 4, see the official Adobe Flex Team blog.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/07/25/exporting-a-textflow-object-in-flex-4/ -->
<s:Application name="Spark_TextConverter_export_test"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/halo"
        xmlns:comps="comps.*">
    <s:layout>
        <s:VerticalLayout paddingLeft="20" paddingRight="20"
                paddingTop="20" paddingBottom="20" />
    </s:layout>
 
    <fx:Script>
        <![CDATA[
            import flashx.textLayout.conversion.ConversionType;
            import flashx.textLayout.conversion.TextConverter;
        ]]>
    </fx:Script>
 
    <comps:CustomEditor id="customEditor" />
 
    <s:HGroup>
        <s:Button id="htmlBtn"
                label="Export as HTML"
                click="debug.text = TextConverter.export(customEditor.editor.textFlow,
                                        TextConverter.HTML_FORMAT,
                                        ConversionType.STRING_TYPE).toString();" />
        <s:Button id="plainTxtBtn"
                label="Export as plain text"
                click="debug.text = TextConverter.export(customEditor.editor.textFlow,
                                        TextConverter.PLAIN_TEXT_FORMAT,
                                        ConversionType.STRING_TYPE).toString();" />
        <s:Button id="tlfBtn"
                label="Export as TLF"
                click="debug.text = TextConverter.export(customEditor.editor.textFlow,
                                        TextConverter.TEXT_LAYOUT_FORMAT,
                                        ConversionType.STRING_TYPE).toString();" />
    </s:HGroup>
 
    <s:TextArea id="debug" width="100%" height="100%" />
 
</s:Application>

View source is enabled in the following example.

And the custom rich text editor component, comps/CustomEditor.mxml, is as follows:

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/07/25/exporting-a-textflow-object-in-flex-4/ -->
<s:Panel name="CustomEditor"
         xmlns:fx="http://ns.adobe.com/mxml/2009"
         xmlns:s="library://ns.adobe.com/flex/spark"
         xmlns:mx="library://ns.adobe.com/flex/halo"
        title="SimpleTextEditor" minWidth="400">
    <s:layout>
        <s:VerticalLayout gap="0" />
    </s:layout>
 
    <fx:Script>
        <![CDATA[
            import flashx.textLayout.conversion.ConversionType;
            import flashx.textLayout.conversion.TextConverter;
            import flash.text.engine.FontPosture;
            import flash.text.engine.FontWeight;
            import flashx.textLayout.formats.TextAlign;
            import flashx.textLayout.formats.TextDecoration;
            import flashx.textLayout.formats.TextLayoutFormat;
            import mx.events.ColorPickerEvent;
            import mx.events.FlexEvent;
            import spark.events.IndexChangeEvent;
 
            protected function editor_selectionChangeHandler(evt:FlexEvent):void {
                var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                fontDDL.selectedItem = txtLayFmt.fontFamily;
                sizeDDL.selectedItem = txtLayFmt.fontSize;
                boldBtn.selected = (txtLayFmt.fontWeight == FontWeight.BOLD);
                italBtn.selected = (txtLayFmt.fontStyle == FontPosture.ITALIC);
                underBtn.selected = (txtLayFmt.textDecoration == TextDecoration.UNDERLINE);
                colorCP.selectedColor = txtLayFmt.color;
                lineBtn.selected = txtLayFmt.lineThrough;
 
                switch (txtLayFmt.textAlign) {
                    case TextAlign.LEFT:
                        txtAlignBB.selectedIndex = 0;
                        break;
                    case TextAlign.CENTER:
                        txtAlignBB.selectedIndex = 1;
                        break;
                    case TextAlign.RIGHT:
                        txtAlignBB.selectedIndex = 2;
                        break;
                    case TextAlign.JUSTIFY:
                        txtAlignBB.selectedIndex = 3;
                        break;
                    default:
                        txtAlignBB.selectedIndex = -1;
                        break;
                }
            }
 
            protected function fontDDL_changeHandler(evt:IndexChangeEvent):void {
                var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                txtLayFmt.fontFamily = fontDDL.selectedItem;
                editor.setFormatOfRange(txtLayFmt,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                editor.setFocus();
            }
 
            protected function sizeDDL_changeHandler(evt:IndexChangeEvent):void {
                var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                txtLayFmt.fontSize = sizeDDL.selectedItem;
                editor.setFormatOfRange(txtLayFmt,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                editor.setFocus();
            }
 
            protected function boldBtn_clickHandler(evt:MouseEvent):void {
                var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                txtLayFmt.fontWeight = (txtLayFmt.fontWeight == FontWeight.BOLD) ? FontWeight.NORMAL : FontWeight.BOLD;
                editor.setFormatOfRange(txtLayFmt,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                editor.setFocus();
            }
 
            protected function italBtn_clickHandler(evt:MouseEvent):void {
                var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                txtLayFmt.fontStyle = (txtLayFmt.fontStyle == FontPosture.ITALIC) ? FontPosture.NORMAL : FontPosture.ITALIC;
                editor.setFormatOfRange(txtLayFmt,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                editor.setFocus();
            }
 
            protected function underBtn_clickHandler(evt:MouseEvent):void {
                var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                txtLayFmt.textDecoration = (txtLayFmt.fontStyle == TextDecoration.UNDERLINE) ? TextDecoration.NONE : TextDecoration.UNDERLINE;
                editor.setFormatOfRange(txtLayFmt,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                editor.setFocus();
            }
 
            protected function colorCP_changeHandler(evt:ColorPickerEvent):void {
                var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                txtLayFmt.color = colorCP.selectedColor;
                editor.setFormatOfRange(txtLayFmt,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                editor.setFocus();
            }
 
            protected function txtAlignBB_changeHandler(evt:IndexChangeEvent):void {
                if (txtAlignBB.selectedItem) {
                    var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                        editor.selectionAnchorPosition,
                                        editor.selectionActivePosition);
                    txtLayFmt.textAlign = txtAlignBB.selectedItem.value;
                    editor.setFormatOfRange(txtLayFmt,
                                        editor.selectionAnchorPosition,
                                        editor.selectionActivePosition);
                    editor.setFocus();
                }
            }
 
            protected function lineBtn_clickHandler(evt:MouseEvent):void {
                var txtLayFmt:TextLayoutFormat = editor.getFormatOfRange(null,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                txtLayFmt.lineThrough = lineBtn.selected;
                editor.setFormatOfRange(txtLayFmt,
                                    editor.selectionAnchorPosition,
                                    editor.selectionActivePosition);
                editor.setFocus();
            }
        ]]>
    </fx:Script>
 
    <s:TextArea id="editor"
            focusEnabled="false"
            width="100%" height="100%"
            minHeight="200"
            selectionChange="editor_selectionChangeHandler(event);">
        <s:textFlow>
            <s:TextFlow paragraphSpaceBefore="20">
                <s:p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et nibh lorem. Nulla ut velit magna. Nunc quis libero ac orci porta tincidunt eget in lorem. Aenean vitae nisi vitae urna lacinia congue. Duis nec leo turpis. Phasellus dui orci, lacinia in dictum lacinia, ullamcorper a tortor. Suspendisse lacinia, turpis vel euismod gravida, turpis dui vulputate libero, vel consequat enim sem nec mauris. Curabitur vitae magna vel neque accumsan commodo vitae quis ipsum. Nullam ac condimentum elit. Integer eget magna ac mi fermentum luctus. Ut pharetra auctor pulvinar. Duis lobortis, nulla at vestibulum tincidunt, ante neque scelerisque risus, ac dignissim nunc nisl rhoncus risus. Cras pretium egestas purus, a commodo nunc vehicula at. Fusce vestibulum enim in mi hendrerit a viverra justo tempor. Maecenas eget ipsum ac mauris dictum congue eu id justo.</s:p>
                <s:p>Aliquam tincidunt tempor nisi id porta. Aenean risus dolor, tincidunt a ultrices in, laoreet eu ante. Mauris vel lacus neque, ut scelerisque eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec vel lacus sit amet erat vehicula malesuada id in augue. Sed purus massa, placerat non imperdiet nec, venenatis a nulla. Donec vel ligula leo, in rhoncus arcu. Duis semper bibendum facilisis. Duis nibh lorem, egestas rutrum tincidunt non, vulputate accumsan nulla. Nunc ligula nisl, ultrices ut tempor quis, rutrum et enim. Nullam accumsan scelerisque ante id pretium. Mauris nibh metus, blandit in varius congue, pharetra sit amet sem. Phasellus tincidunt lacus quis est semper ut rhoncus sem pretium. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar, enim eu consectetur venenatis, dui tortor commodo ante, sit amet sagittis libero odio cursus neque. Aliquam a dui non eros placerat euismod. In at mattis felis. Suspendisse potenti. Morbi posuere condimentum lacus. Suspendisse tellus magna, viverra ac mattis vel, adipiscing eget lectus.</s:p>
                <s:p>Etiam ut eros lectus. Praesent nec massa nibh. Cras venenatis, ligula in condimentum euismod, nisl lorem hendrerit lacus, a imperdiet odio est et odio. Suspendisse eu orci ut augue commodo gravida sed eu risus. Vestibulum venenatis erat ac metus ullamcorper blandit. Integer et sem enim. Vivamus a arcu metus. Nunc sollicitudin commodo placerat. Maecenas vehicula, massa et auctor tempor, felis leo commodo lorem, eget pulvinar felis turpis nec erat. Mauris imperdiet gravida felis a eleifend.</s:p>
                <s:p>Suspendisse mattis tempor fringilla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque sed molestie arcu. Praesent ut tellus sed orci blandit tristique non eget est. Sed interdum feugiat nisi, sit amet aliquet enim sodales non. Maecenas in velit sit amet tellus tincidunt dapibus. Vivamus est eros, iaculis et venenatis a, malesuada vel lacus. Aliquam vel orci tortor. Etiam ornare ante eget massa dignissim a auctor nunc pellentesque. Pellentesque sodales porta nisi, pretium accumsan eros tincidunt vitae. Cras facilisis accumsan purus ultricies lacinia. Praesent consequat elit imperdiet tellus vehicula ut ornare mauris mattis. Suspendisse non tortor nisl. Etiam ac pretium est.</s:p>
                <s:p>Maecenas tristique, velit aliquam faucibus ornare, justo erat porta elit, sed venenatis neque mi ac elit. Nullam enim metus, gravida ac euismod sit amet, commodo vitae elit. Quisque eget molestie ante. Nulla fermentum pretium augue non tristique. Praesent in orci eu diam ultrices sodales ac quis leo. Aliquam lobortis elit quis mi rutrum feugiat. Aenean sed elit turpis. Duis enim ligula, posuere sit amet semper a, pretium vel leo. Etiam mollis dolor nec elit suscipit imperdiet. Sed a est eros.</s:p>
            </s:TextFlow>
        </s:textFlow>
    </s:TextArea>
    <mx:ControlBar width="100%" cornerRadius="0">
        <mx:ToolBar width="100%" horizontalGap="5">
            <s:DropDownList id="fontDDL"
                    width="150"
                    change="fontDDL_changeHandler(event);">
                <s:dataProvider>
                    <s:ArrayList source="[Arial,Verdana,Times New Roman,Trebuchet MS]" />
                </s:dataProvider>
            </s:DropDownList>
            <s:DropDownList id="sizeDDL"
                    width="60"
                    change="sizeDDL_changeHandler(event);">
                <s:dataProvider>
                    <s:ArrayList source="[8,10,12,14,16,24,36,72]" />
                </s:dataProvider>
            </s:DropDownList>
            <s:ToggleButton id="boldBtn"
                    label="B"
                    fontWeight="bold"
                    width="30"
                    click="boldBtn_clickHandler(event);" />
            <s:ToggleButton id="italBtn"
                    label="I"
                    fontStyle="italic"
                    width="30"
                    click="italBtn_clickHandler(event);" />
            <s:ToggleButton id="underBtn"
                    label="U" 
                    textDecoration="underline"
                    width="30"
                    click="underBtn_clickHandler(event);" />
            <s:ToggleButton id="lineBtn"
                    label="S"
                    lineThrough="true"
                    width="30"
                    click="lineBtn_clickHandler(event);" />
            <mx:ColorPicker id="colorCP"
                    change="colorCP_changeHandler(event);" />
            <s:ButtonBar id="txtAlignBB"
                    arrowKeysWrapFocus="true"
                    labelField="label"
                    width="120"
                    change="txtAlignBB_changeHandler(event);">
                <s:dataProvider>
                    <s:ArrayList>
                        <fx:Object label="L" value="{TextAlign.LEFT}" />
                        <fx:Object label="C" value="{TextAlign.CENTER}" />
                        <fx:Object label="R" value="{TextAlign.RIGHT}" />
                        <fx:Object label="J" value="{TextAlign.JUSTIFY}" />
                    </s:ArrayList>
                </s:dataProvider>
            </s:ButtonBar>
        </mx:ToolBar>
    </mx:ControlBar>
 
</s:Panel>

This entry is based on a beta version of the Flex 4 SDK and therefore is very likely to change as development of the Flex SDK continues. The API can (and will) change causing examples to possibly not compile in newer versions of the Flex 4 SDK.

 

Posted by 1010
반응형
Posted by 1010
반응형

SparkRichTextEditor is a flex component based on Text Layout Framework with basic functionality implementation of a simple text editor with styles and key shortcuts with advances and improvements of new text system of Adobe.

Source codeClick with the right button to obtain source code.

More great examples of Adobe TLF and from tlftexteditor (in Google Code).

Posted by 1010
반응형

Print DataGrid In Flex


PrintDataGridExample.mxml

<?xml version="1.0"?>
<!-- Main application to print a DataGrid control on multiple pages. -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="initData();">
<mx:Script>
<![CDATA[
import mx.printing.*;
import mx.collections.ArrayCollection;
import FormPrintView;

// Declare variables and initialize simple variables.
[Bindable]
public var dgProvider:ArrayCollection;
public var footerHeight:Number=20;
public var prodIndex:Number;
public var prodTotal:Number=0;

// Data initialization.
public function initData():void
{
// Create the data provider for the DataGrid control.
dgProvider=new ArrayCollection;
}

// Fill the dgProvider ArrayCollection with the specified items.
public function setdgProvider(items:int):void
{

prodIndex=1;
dgProvider.removeAll();
for (var z:int=0; z < items; z++)
{
var prod1:Object={};
prod1.Qty=prodIndex * 7;
prod1.Index=prodIndex++;
prodTotal+=prod1.Qty;
dgProvider.addItem(prod1);
}
}

// The function to print the output.
public function doPrint():void
{

var printJob:FlexPrintJob=new FlexPrintJob();
if (printJob.start())
{
// Create a FormPrintView control as a child of the current view.
var thePrintView:FormPrintView=new FormPrintView();
Application.application.addChild(thePrintView);

//Set the print view properties.
thePrintView.width=printJob.pageWidth;
thePrintView.height=printJob.pageHeight;
thePrintView.prodTotal=prodTotal;
// Set the data provider of the FormPrintView component's data grid
// to be the data provider of the displayed data grid.
thePrintView.myDataGrid.dataProvider=myDataGrid.dataProvider;
// Create a single-page image.
thePrintView.showPage("single");
// If the print image's data grid can hold all the provider's rows,
// add the page to the print job.
if (!thePrintView.myDataGrid.validNextPage)
{
printJob.addObject(thePrintView);
}
// Otherwise, the job requires multiple pages.
else
{
// Create the first page and add it to the print job.
thePrintView.showPage("first");
printJob.addObject(thePrintView);
thePrintView.pageNumber++;
// Loop through the following code until all pages are queued.
while (true)
{
// Move the next page of data to the top of the print grid.
thePrintView.myDataGrid.nextPage();
thePrintView.showPage("last");
// If the page holds the remaining data, or if the last page
// was completely filled by the last grid data, queue it for printing.
// Test if there is data for another PrintDataGrid page.
if (!thePrintView.myDataGrid.validNextPage)
{
// This is the last page; queue it and exit the print loop.
printJob.addObject(thePrintView);
break;
}
else
// This is not the last page. Queue a middle page.
{
thePrintView.showPage("middle");
printJob.addObject(thePrintView);
thePrintView.pageNumber++;
}
}
}
// All pages are queued; remove the FormPrintView control to free memory.
Application.application.removeChild(thePrintView);
}
// Send the job to the printer.
printJob.send();
}
]]>
</mx:Script>

<mx:Panel title="DataGrid Printing Example" height="75%" width="75%" paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10">

<mx:DataGrid id="myDataGrid" dataProvider="{dgProvider}">
<mx:columns>
<mx:DataGridColumn dataField="Index"/>
<mx:DataGridColumn dataField="Qty"/>
</mx:columns>
</mx:DataGrid>

<mx:Text width="100%" color="blue" text="Specify the number of lines and click Fill Grid first. Then you can click Print."/>

<mx:TextInput id="dataItems" text="35"/>

<mx:HBox>
<mx:Button id="setDP" label="Fill Grid" click="setdgProvider(int(dataItems.text));"/>
<mx:Button id="printDG" label="Print" click="doPrint();"/>
</mx:HBox>
</mx:Panel>
</mx:Application>


FormPrintView.mxml

<?xml version="1.0"?>
<!-- Custom control to print the DataGrid control on multiple pages. -->

<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" backgroundColor="#FFFFFF" paddingTop="50" paddingBottom="50" paddingLeft="50">

<mx:Script>
<![CDATA[
import mx.core.*
// Declare and initialize the variables used in the component.
// The application sets the actual prodTotal value.
[Bindable]
public var pageNumber:Number=1;
[Bindable]
public var prodTotal:Number=0;

// Control the page contents by selectively hiding the header and
// footer based on the page type.
public function showPage(pageType:String):void
{
if (pageType == "first" || pageType == "middle")
{
// Hide the footer.
footer.includeInLayout=false;
footer.visible=false;
}
if (pageType == "middle" || pageType == "last")
{
// The header won't be used again; hide it.
header.includeInLayout=false;
header.visible=false;
}
if (pageType == "last")
{
// Show the footer.
footer.includeInLayout=true;
footer.visible=true;
}
//Update the DataGrid layout to reflect the results.
validateNow();
}
]]>
</mx:Script>

<!-- The template for the printed page, with the contents for all pages. -->
<mx:VBox width="80%" horizontalAlign="left">
<mx:Label text="Page {pageNumber}"/>
</mx:VBox>

<FormPrintHeader id="header"/>
<!-- The data grid. The sizeToPage property is true by default, so the last
page has only as many grid rows as are needed for the data. -->
<mx:PrintDataGrid id="myDataGrid" width="60%" height="100%">
<!-- Specify the columns to ensure that their order is correct. -->
<mx:columns>
<mx:DataGridColumn dataField="Index"/>
<mx:DataGridColumn dataField="Qty"/>
</mx:columns>
</mx:PrintDataGrid>

<!-- Create a FormPrintFooter control and set its prodTotal variable. -->
<FormPrintFooter id="footer" pTotal="{prodTotal}"/>

</mx:VBox>


FormPrintHeader.mxml

<?xml version="1.0"?>
<!-- Custom control for the header area of the printed page. -->

<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="60%" horizontalAlign="right">

<mx:Label text="This is a placeholder for first page contents"/>
</mx:VBox>


FormPrintFooter.mxml

<?xml version="1.0"?>
<!-- Custom control for the footer area of the printed page. -->

<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="60%" horizontalAlign="right">
<!-- Declare and initialize the product total variable. -->

<mx:Script>
<![CDATA[
[Bindable]
public var pTotal:Number=0;
]]>
</mx:Script>
<mx:Label text="Product Total: {pTotal}"/>
</mx:VBox>


Examples:-

Posted by 1010
반응형
FlexPrintJob

패키지 mx.printing
클래스 public class FlexPrintJob
상속 FlexPrintJob Inheritance Object

언어 버전:  ActionScript 3.0
제품 버전:  Flex 3
런타임 버전:  Flash Player 9, AIR 1.1

 

Was this helpful?Yes   No

 

 

The FlexPrintJob class is a wrapper for the flash.printing.PrintJob class. It supports automatically slicing and paginating the output on multilple pages, and scaling the grid contents to fit the printer's page size.

 

예제 보기

기타 예제

추가 정보


 


공용 속성
  속성 정의 주체
  Inherited constructor : Object
지정된 객체 인스턴스의 클래스 객체 또는 생성자 함수에 대한 참조입니다.
Object
    pageHeight : Number
[읽기 전용] The height of the printable area on the printer page; it does not include any user-set margins.
FlexPrintJob
    pageWidth : Number
[읽기 전용] The width of the printable area on the printer page; it does not include any user-set margins.
FlexPrintJob
    printAsBitmap : Boolean
Specifies whether to print the job content as a bitmap (true) or in vector format (false).
FlexPrintJob
공용 메서드
  메서드 정의 주체
   
Constructor.
FlexPrintJob
   
addObject(obj:IUIComponent, scaleType:String = "matchWidth"):void
Adds a UIComponent object to the list of objects being printed.
FlexPrintJob
  Inherited
지정된 속성이 객체에 정의되어 있는지 여부를 나타냅니다.
Object
  Inherited
Object 클래스의 인스턴스가 매개 변수로 지정된 객체의 프로토타입 체인에 있는지 여부를 나타냅니다.
Object
  Inherited
지정된 속성이 존재하고 열거 가능한지 여부를 나타냅니다.
Object
   
Sends the added objects to the printer to start printing.
FlexPrintJob
  Inherited
루프 작업에서 동적 속성을 사용할 수 있는지 여부를 설정합니다.
Object
   
Initializes the PrintJob object.
FlexPrintJob
  Inherited
로캘별 규칙에 따라 서식이 지정된 이 객체의 문자열 표현을 반환합니다.
Object
  Inherited
지정된 객체의 문자열 표현을 반환합니다.
Object
  Inherited
지정된 객체의 프리미티브 값을 반환합니다.
Object
속성 세부 정보

pageHeight

속성
pageHeight:Number  [읽기 전용]

 

언어 버전:  ActionScript 3.0
제품 버전:  Flex 3
런타임 버전:  Flash Player 9, AIR 1.1

 

 

The height of the printable area on the printer page; it does not include any user-set margins. It is set after start() method returns.



구현
    public function get pageHeight():Number

pageWidth

속성  
pageWidth:Number  [읽기 전용]

 

언어 버전:  ActionScript 3.0
제품 버전:  Flex 3
런타임 버전:  Flash Player 9, AIR 1.1

 

 

The width of the printable area on the printer page; it does not include any user-set margins. This property is set after start() method returns.



구현
    public function get pageWidth():Number

printAsBitmap

속성  
printAsBitmap:Boolean

언어 버전:  ActionScript 3.0
제품 버전:  Flex 3
런타임 버전:  Flash Player 9, AIR 1.1

 

 

Specifies whether to print the job content as a bitmap (true) or in vector format (false). Printing as a bitmap supports output that includes a bitmap image with alpha transparency or color effects. If the content does not include any bitmap images with alpha transparency or color effects, you can print in higher quality vector format by setting the printAsBitmap property to false.

기본값: true.



구현
    public function get printAsBitmap():Boolean
    public function set printAsBitmap(value:Boolean):void
생성자 세부 정보

FlexPrintJob

() 생성자
public function FlexPrintJob()

 

언어 버전:  ActionScript 3.0
제품 버전:  Flex 3
런타임 버전:  Flash Player 9, AIR 1.1

 

Constructor.

메서드 세부 정보

addObject

() 메서드
public function addObject(obj:IUIComponent, scaleType:String = "matchWidth"):void

언어 버전:  ActionScript 3.0
제품 버전:  Flex 3
런타임 버전:  Flash Player 9, AIR 1.1

 

 

Adds a UIComponent object to the list of objects being printed. Call this method after the start() method returns. Each call to this method starts a new page, so you should format your objects in page-sized chunks. You can use the PrintDataGrid class to span a data grid across multiple pages.

매개 변수

obj:IUIComponent — The Object to be printed.
 
scaleType:String (default = "matchWidth") — The scaling technique to use to control how the object fits on one or more printed pages. Must be one of the constant values defined in the FlexPrintJobScaleType class.

 

관련 API 요소

send

() 메서드  
public function send():void

언어 버전:  ActionScript 3.0
제품 버전:  Flex 3
런타임 버전:  Flash Player 9, AIR 1.1

 

 

Sends the added objects to the printer to start printing. Call this method after you have used the addObject() method to add the print pages.

start

() 메서드  
public function start():Boolean

언어 버전:  ActionScript 3.0
제품 버전:  Flex 3
런타임 버전:  Flash Player 9, AIR 1.1

 

 

Initializes the PrintJob object. Displays the operating system printer dialog to the user. Flex sets the pageWidth and pageHeight properties after this call returns.

 

반환값
Booleantrue if the user clicks OK when the print dialog box appears, or false if the user clicks Cancel or if an error occurs.
FormPrintHeader.mxml
<?xml version="1.0"?>
<!-- Custom control for the header area of the printed page. -->
<s:VGroup name="FormPrintHeader"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        width="60%"
        horizontalAlign="right" >

    <s:Label text="This is a placeholder for first page contents"/>

</s:VGroup>
FormPrintFooter.mxml
<?xml version="1.0"?>
<!-- Custom control for the footer area of the printed page. -->
<s:VGroup name="FormPrintFooter"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        width="60%"
        horizontalAlign="right" >

    <!-- Declare and initialize the product total variable. -->
    <fx:Script>
        <![CDATA[
            [Bindable]
            public var pTotal:Number = 0;
        ]]>
    </fx:Script>

    <s:Label text="Product Total: {pTotal}"/>

</s:VGroup>
FormPrintView.mxml
<?xml version="1.0"?>
<!-- Custom control to print the Halo DataGrid control on multiple pages. -->
<s:VGroup name="FormPrintView"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx"
        xmlns="*">

    <fx:Script>
        <![CDATA[
            import mx.core.*;

            // Declare and initialize the variables used in the component.
            // The application sets the actual prodTotal value.
            [Bindable]
            public var pageNumber:Number = 1;

            [Bindable]
            public var prodTotal:Number = 0;

            // Control the page contents by selectively hiding the header and
            // footer based on the page type.
            public function showPage(pageType:String):void {
                if (pageType == "first" || pageType == "middle") {
                    // Hide the footer.
                    footer.includeInLayout = false;
                    footer.visible = false;
                }
                if (pageType == "middle" || pageType == "last") {
                    // The header won't be used again; hide it.
                    header.includeInLayout = false;
                    header.visible = false;
                }
                if (pageType == "last") {
                    // Show the footer.
                    footer.includeInLayout = true;
                    footer.visible = true;
                }
                //Update the DataGrid layout to reflect the results.
                validateNow();
            }
        ]]>
    </fx:Script>

    <!-- The template for the printed page, with the contents for all pages. -->
    <s:VGroup width="80%" horizontalAlign="left">
        <s:Label text="Page {pageNumber}"/>
    </s:VGroup>

    <FormPrintHeader id="header" />

    <!-- The data grid. The sizeToPage property is true by default, so the last
        page has only as many grid rows as are needed for the data. -->
    <mx:PrintDataGrid id="myDataGrid" width="60%" height="100%">
        <!-- Specify the columns to ensure that their order is correct. -->
        <mx:columns>
            <mx:DataGridColumn dataField="Index" />
            <mx:DataGridColumn dataField="Qty" />
        </mx:columns>
    </mx:PrintDataGrid>

    <!-- Create a FormPrintFooter control and set its prodTotal variable. -->
    <FormPrintFooter id="footer" pTotal="{prodTotal}" />

</s:VGroup>
PrintDataGridExample.mxml
<?xml version="1.0"?>
<!-- Main application to print a Halo DataGrid control on multiple pages. -->
<s:Application name="PrintDataGridExample.mxml"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx"
        initialize="initData();">

    <fx:Script>
        <![CDATA[

        import mx.printing.*;
        import mx.collections.ArrayCollection;
        import FormPrintView;
        import mx.core.FlexGlobals;

        // Declare variables and initialize simple variables.
        [Bindable]
        public var dgProvider:ArrayCollection;
        public var footerHeight:Number = 20;
        public var prodIndex:Number;
        public var prodTotal:Number = 0;


        // Data initialization.
        public function initData():void {
            // Create the data provider for the DataGrid control.
            dgProvider = new ArrayCollection;
        }

        // Fill the dgProvider ArrayCollection with the specified items.
        public function setdgProvider(items:int):void {
            prodIndex=1;
            dgProvider.removeAll();
            for (var z:int=0; z<items; z++) {
                var prod1:Object = {};
                prod1.Qty = prodIndex * 7;
                prod1.Index = prodIndex++;
                prodTotal += prod1.Qty;
                dgProvider.addItem(prod1);
            }
        }

        // The function to print the output.
        public function doPrint():void {
            var printJob:FlexPrintJob = new FlexPrintJob();
            if (printJob.start()) {
                // Create a FormPrintView control as a child of the current view.
                var thePrintView:FormPrintView = new FormPrintView();
                FlexGlobals.topLevelApplication.addElement(thePrintView);

                //Set the print view properties.
                thePrintView.width=printJob.pageWidth;
                thePrintView.height=printJob.pageHeight;
                thePrintView.prodTotal = prodTotal;
                // Set the data provider of the FormPrintView component's data grid
                // to be the data provider of the displayed data grid.
                thePrintView.myDataGrid.dataProvider = myDataGrid.dataProvider;
                // Create a single-page image.
                thePrintView.showPage("single");
                // If the print image's data grid can hold all the provider's rows,
                // add the page to the print job.
                if (!thePrintView.myDataGrid.validNextPage)  {
                    printJob.addObject(thePrintView);
                }
                // Otherwise, the job requires multiple pages.
                else {
                    // Create the first page and add it to the print job.
                    thePrintView.showPage("first");
                    printJob.addObject(thePrintView);
                    thePrintView.pageNumber++;
                    // Loop through the following code until all pages are queued.
                    while (true) {
                        // Move the next page of data to the top of the print grid.
                        thePrintView.myDataGrid.nextPage();
                        thePrintView.showPage("last");
                        // If the page holds the remaining data, or if the last page
                        // was completely filled by the last grid data, queue it for printing.
                        // Test if there is data for another PrintDataGrid page.
                        if (!thePrintView.myDataGrid.validNextPage) {
                            // This is the last page; queue it and exit the print loop.
                            printJob.addObject(thePrintView);
                            break;
                        } else {
                            // This is not the last page. Queue a middle page.
                            thePrintView.showPage("middle");
                            printJob.addObject(thePrintView);
                            thePrintView.pageNumber++;
                        }
                    }
                }
                // All pages are queued; remove the FormPrintView control to free memory.
                FlexGlobals.topLevelApplication.removeElement(thePrintView);
            }
            // Send the job to the printer.
            printJob.send();
        }
        ]]>
    </fx:Script>

    <s:Panel title="DataGrid Printing Example"
            width="75%" height="75%"
            horizontalCenter="0" verticalCenter="0">
        <s:VGroup left="10" right="10" top="10" bottom="10">
            <mx:DataGrid id="myDataGrid" dataProvider="{dgProvider}">
                <mx:columns>
                    <mx:DataGridColumn dataField="Index"/>
                    <mx:DataGridColumn dataField="Qty"/>
                </mx:columns>
            </mx:DataGrid>

            <s:Label width="100%" color="blue"
                text="Specify the number of lines and click Fill Grid first. Then you can click Print."/>

            <s:TextInput id="dataItems" text="35"/>

            <s:HGroup>
                <s:Button id="setDP" label="Fill Grid" click="setdgProvider(int(dataItems.text));"/>
                <s:Button id="printDG" label="Print" click="doPrint();"/>
            </s:HGroup>
        </s:VGroup>
    </s:Panel>

</s:Application>
Posted by 1010
반응형


Flash CS3에서 Flex Component Skin 제작하기

[출처] http://blog.naver.com/ang_/30029082083

자세한 제작 내용은 위 출처를 참고해주세요. 여기서는 Flex Skin Templetes 자료와 준비 과정까지만을 포스팅 하겠습니다.

Flash CS3에서 Flex Component Skin을 제작하기 위해서는 직접 플래쉬로 구현하는 방법도 있겠지만 초보자들에겐 쉽지만은 않은 방법입니다. 그래서 좀 더 편한 방법으로 제공되어진게 바로 미리 제작되어진 Templetes를 이용하는 방법이 있습니다.

우선 제작하기 위해선 flex skin 파일을 다운로드 받으셔야 합니다.
다운로드 - http://www.adobe.com/cfusion/entitlement/index.cfm?e=flex_skins#fxcompkit

위 사이트로 가시면 Flex Skin Design Extension for Flash에 있는 파일을 다운받으시기 바랍니다.

dlDownload for Windows and Macintosh (MXP, 4.44 MB) <--- 귀찮으신 분들은 여기를 클릭


(링크가 깨졌을 경우 아래 링크로 다운받으세요.)



를 다운 받으셨으면 이제 CS3 Extension Manager를 실행합니다.
Extension Manager은 아래 경로에 존재합니다.
C:\Program Files\Adobe\Adobe Extension Manager\Extension Manager.exe
Adobe Master Collection CS3 을 설치 하신 분들이라면
시작 → 모든프로그램 → Adobe Master Collection CS3 → Adobe Extension Manager CS3

(다운로드 받은 파일을 더블클릭하시면 자동으로 Extension Manager가 실행 되지만 만약 CS4를 같이 설치하신분이라면
CS4용 Adobe Extension Manager가 실행 되니 위에 나와있는 방법으로 실행하시면 됩니다.)


Extension Manager를 실행하시면 위와 같은 실행화면을 보실 수 있습니다.
 버튼을 클릭하여 다운받은
 파일을 선택하여 줍니다.


동의 창이 나오면 동의버튼을
  클릭합니다.


설치 화면이 나오고 설치 성공 화면이 나오면 확인 버틍을 클릭합니다.



Extension Manager를 종료하고 Flash CS3를 실행 시킵니다.



위 처럼 Flex Skins 라는 메뉴가 나오면 스킨 만들 준비 끝~~
그럼 이제 스킨 템플릿 파일을 열어 보겠습니다.


위와 같이 여러 콤포넌트들의 스킨을 제작할 수 있습니다.
여기서 flex_skins 파일을 선택합니다.


flex의 기본 콤포넌트의 스킨들이 모두 모아져 있습니다. 여기서 스킨을 수정 하여 swc 파일로 Export 시키셔서 flex에서 사용하시면 됩니다. 제작법과 flex에서 불러오는 법은 행복한앙님의 블로그에 자세히 나와 있으니 참고 하시기 바랍니다.
행복한앙님 블로그의 게시물 : http://blog.naver.com/ang_/30029082083

 

Posted by 1010
반응형

Adobe Flex 3 Help

Printing multipage output

You can print well-formatted multipage output under the following conditions:

  • When each control fits on a print page or less. You often encounter such jobs when printing a form with fixed-length fields.
  • When the printed output includes one or more PrintDataGrid controls that are too long to print on a single page, particularly if the control height might vary, depending on the data. A good example of this type of output is a customer order receipt, which starts with the customer information, has an indeterminate number of order line items, and ends with total information.

Printing known-length multipage output

If you know the length of each component in a multipage document, you can create a separate print layout component for each page you print, and specify each layout page in a separate addObject() method, as follows:

printJob.addObject(introPrintView, "ShowAll");
printJob.addObject(finDetailPrintView, "ShowAll");
printJob.addObject(hrDetailPrintView, "ShowAll");
printJob.addObject(summaryPrintView, "ShowAll");

Using the PrintDataGrid control for multipage grids

When a DataGrid control with many rows does not fit on a single screen in your application, you typically have scroll bars that let users view all the data. When you print the DataGrid control, the output is the same as the screen display. Therefore, if your DataGrid control has rows or columns that are not immediately visible, they do not print. If you replace the DataGrid control with a PrintDataGrid control that does not have a height specified (or has a large height), you print all the rows, but some rows could be partially printed on the bottom of one page and partially printed at the top of another, as you often see with HTML printout.

You can solve these problems by using the following features of the PrintDataGrid control. These features let you correctly print grids that contain multiple pages of data without splitting rows across pages:

sizeToPage property 

Makes the printed data grid contain only full rows.



nextPage() method 

Gets the next printable page of data.



validNextPage property 

Is true if printing the data requires an additional page.



Using the sizeToPage attribute to format pages

A PrintDataGrid page consists of the rows that are visible in the control's current view. Suppose, for example, that a PrintDataGrid control has a height of 130 pixels. The total height of each row and header is 30 pixels, and the control's data provider has 10 rows. In this situation, the printed PrintDataGrid page contains only three complete data rows, plus the header. The sizeToPage property specifies whether to include a fourth and partial data row.

The sizeToPage property, which is true by default, causes the PrintDataGrid control to remove any partially visible or empty rows and to resize itself to include only complete rows in the current view. For the data grid described in the preceding paragraph, when this property is true, the DataGrid shrinks to show three complete data rows, and no incomplete rows; if the attribute is false, the grid includes a partial row at the bottom.

The following properties provide information on page sizing that are affected by the sizeToPage property:

Property

Description

currentPageHeight

Contains the height of the grid, in pixels, that results if the sizeToPage property is true. If the sizeToPage property is true, the currentPageHeight property equals the height property.

originalHeight

Contains the grid height that results if the sizeToPage property is false. If the sizeToPage property is false, the originalHeight property equals the height property.

In most applications, you leave the sizeToPage attribute at its default value (true), and use the height property to determine the grid height.

The sizeToPage property does not affect the way the page breaks when a single PrintDataGrid control page is longer than a print page. To print multipage data grids without splitting rows, you must divide the grid items into multiple views by using the nextPage() method, as described in Using the nextPage() method and validNextPage property to print multiple pages.

Using the nextPage() method and validNextPage property to print multiple pages

The validNextPage property is true if the PrintDataGrid control has data beyond the rows that fit on the current print page. You use it to determine whether you need to format and print an additional page.

The nextPage() method lets you page through the data provider contents by setting the first row of the PrintDataGrid control to be the data provider row that follows the last row of the previous PrintDataGrid page. In other words, the nextPage() method increases the grid's verticalScrollPosition property by the value of the grid's rowCount property.

The following code shows a loop that prints a grid using multiple pages, without having rows that span pages:

// Queue the first page.
printJob.addObject(thePrintView);
// While there are more pages, print them.
while (thePrintView.myDataGrid.validNextPage) {
    //Put the next page of data in the view.
    thePrintView.myDataGrid.nextPage();
    //Queue the additional page.
    printJob.addObject(thePrintView);
}

The section Example: Printing with multipage PrintDataGrid controls shows how to use the nextPage() method to print a report with a multipage data grid.

Updating the PrintDataGrid layout

When you use a PrintDataGrid control to print a single data grid across multiple pages, you queue each page of the grid individually. If your application customizes each page beyond simply using the nextPage() method to page through the PrintDataGrid, you must call the validateNow() method to update the page layout before you print each page, as shown in Print output component.

Example: Printing with multipage PrintDataGrid controls

The following example prints a data grid in which you can specify the number of items in the data provider. You can, therefore, set the DataGrid control contents to print on one, two, or more pages, so that you can see the effects of different-sized data sets on the printed result.

The example also shows how you can put header information before the grid and footer information after the grid, as in a shipping list or receipt. It uses the technique of selectively showing and hiding the header and footer, depending on the page being printed. To keep the code as short as possible, the example uses simple placeholder information only.

The application consists of the following files:

  • The application file displays the form to the user, including TextArea and Button controls to set the number of lines and a Print button. The file includes the code to initialize the view, get the data, and handle the user's print request. It uses the FormPrintView MXML component as a template for the printed output.
  • The FormPrintView.mxml file formats the printed output. It has two major elements:
    • The print output template includes the PrintDataGrid control and uses two MXML components to format the header and footer contents.
    • The showPage() function determines which sections of the template to include in a particular page of the output, based on the page type: first, middle, last, or single. On the first page of multipage output, the showPage() function hides the footer; on the middle and last pages, it hides the header. On a single page, it shows both header and footer.
  • The FormPrintHeader.mxml and formPrintFooter.mxml files specify the contents of the start and the end of the output. To keep the application simple, the header has a single, static Label control. The footer displays a total of the numbers in the Quantity column. In a more complete application, the header page could have, for example, a shipping address, and the footer page could show more detail about the shipment totals.

The files include detailed comments explaining the purpose of the code.

Multipage print application file

The following code shows the multipage print application file:

<?xml version="1.0"?>
<!-- printing\MultiPagePrint.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    initialize="initData();">

    <mx:Script>
    <![CDATA[
        import mx.printing.*;
        import mx.collections.ArrayCollection;
        // Import the MXML custom print view control.
        import myComponents.FormPrintView;

        // Declare variables and initialize simple variables.
        // The dgProvider ArrayCollection is the DataGrid data provider.
        // It must be bindable because you change its contents dynamically.
        [Bindable]
        public var dgProvider:ArrayCollection;
        public var footerHeight:Number = 20;
        public var prodIndex:Number;
        public var prodTotal:Number = 0;
        
        // Data initialization, called when the application initializes.
        public function initData():void {
            // Create the data provider for the DataGrid control.
            dgProvider = new ArrayCollection; 
        }
        
        // Fill the dgProvider ArrayCollection with the specified items.
        public function setdgProvider(items:int):void { 
            // First initialize the index and clear any existing data.
            prodIndex=1;
            dgProvider.removeAll();
            
            // Fill the ArrayCollection, and calculate a product total.
            // For simplicity, it increases the Index field value by
            // 1, and the Qty field by 7 for each item.
            for (var z:int=0; z<items; z++) 
            {
                var prod1:Object = {};
                prod1.Qty = prodIndex * 7;
                prod1.Index = prodIndex++;
                prodTotal += prod1.Qty; 
                dgProvider.addItem(prod1);
            }
        }

        // The function to print the output.
        public function doPrint():void {
            // Create a FlexPrintJob instance.
            var printJob:FlexPrintJob = new FlexPrintJob();
            
            // Start the print job.
            if (printJob.start()) {
                // Create a FormPrintView control 
                // as a child of the application.
                var thePrintView:FormPrintView = new FormPrintView();
                addChild(thePrintView);
                
                // Set the print view properties.
                thePrintView.width=printJob.pageWidth;
                thePrintView.height=printJob.pageHeight;
                thePrintView.prodTotal = prodTotal;
                
                // Set the data provider of the FormPrintView 
                // component's DataGrid to be the data provider of 
                // the displayed DataGrid.
                thePrintView.myDataGrid.dataProvider = 
                    myDataGrid.dataProvider;
                
                // Create a single-page image.
                thePrintView.showPage("single");
                
                // If the print image's DataGrid can hold all the  
                // data provider's rows, add the page to the print job. 
                if(!thePrintView.myDataGrid.validNextPage)
                {
                    printJob.addObject(thePrintView);
                }
                // Otherwise, the job requires multiple pages.
                else
                {
                    // Create the first page and add it to the print job.
                    thePrintView.showPage("first");
                    printJob.addObject(thePrintView);
                    thePrintView.pageNumber++;
                    
                    // Loop through the following code 
                    // until all pages are queued.
                    while(true)
                    {
                        // Move the next page of data to the top of 
                        // the PrintDataGrid.
                        thePrintView.myDataGrid.nextPage();

                        // Try creating a last page.
                        thePrintView.showPage("last");  

                        // If the page holds the remaining data, or if 
                        // the last page was completely filled by the last  
                        // grid data, queue it for printing.
                        // Test if there is data for another 
                        // PrintDataGrid page.
                        if(!thePrintView.myDataGrid.validNextPage) 
                        {
                            // This is the last page; 
                            // queue it and exit the print loop.
                            printJob.addObject(thePrintView);
                            break;
                        }
                        else
                        // This is not the last page. Queue a middle page. 
                        {
                            thePrintView.showPage("middle");
                            printJob.addObject(thePrintView);
                            thePrintView.pageNumber++;
                        }
                    }
                }
                // All pages are queued; remove the FormPrintView 
                // control to free memory.
                removeChild(thePrintView);
            }
            // Send the job to the printer.
            printJob.send();
        }
    ]]>
    </mx:Script>

    <!-- The form that appears on the user's system.-->
    <mx:Form id="myForm" width="80%">
        <mx:FormHeading label="Product Information"/>
                <mx:DataGrid id="myDataGrid" dataProvider="{dgProvider}">
                <mx:columns>
                    <mx:DataGridColumn dataField="Index"/>
                    <mx:DataGridColumn dataField="Qty"/>
                </mx:columns>
            </mx:DataGrid>
        <mx:Text width="100%" 
            text="Specify the number of lines and click Fill Grid first. 
            Then you can click Print."/>
        <mx:TextInput id="dataItems" text="35"/>
        <mx:HBox>
            <mx:Button id="setDP" 
                label="Fill Grid"
                click="setdgProvider(int(dataItems.text));"/>
            <mx:Button id="printDG" 
                label="Print" 
                click="doPrint();"/>
        </mx:HBox>
    </mx:Form>
</mx:Application>

The executing SWF file for the previous example is shown below:

 

Print output component

The following lines show the FormPrintView.mxml custom component file:

<?xml version="1.0"?>
<!-- printing\myComponents\FormPrintView.mxml -->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:MyComp="myComponents.*" 
    backgroundColor="#FFFFFF"
    paddingTop="50" paddingBottom="50" paddingLeft="50">

    <mx:Script>
        <![CDATA[
            import mx.core.*
            
            // Declare and initialize the variables used in the component.
            // The application sets the actual prodTotal value.
            [Bindable]
            public var pageNumber:Number = 1;
            [Bindable]
            public var prodTotal:Number = 0;

            // Control the page contents by selectively hiding the header and
            // footer based on the page type.
            public function showPage(pageType:String):void {
                if(pageType == "first" || pageType == "middle") {
                    // Hide the footer.
                    footer.includeInLayout=false;
                    footer.visible = false;
                }
                if(pageType == "middle" || pageType == "last") {
                    // The header won't be used again; hide it.
                    header.includeInLayout=false;
                    header.visible = false;
                }
                if(pageType == "last") {
                    // Show the footer.
                    footer.includeInLayout=true;
                    footer.visible = true;
                }
                //Update the DataGrid layout to reflect the results.
                validateNow();
            }
        ]]>
    </mx:Script>

    <!-- The template for the printed page, 
        with the contents for all pages. -->
    <mx:VBox width="80%" horizontalAlign="left">
        <mx:Label text="Page {pageNumber}"/>
    </mx:VBox>
    <MyComp:FormPrintHeader id="header"/>
    
    <!-- The sizeToPage property is true by default, so the last
        page has only as many grid rows as are needed for the data. -->
    <mx:PrintDataGrid id="myDataGrid" width="60%" height="100%">
    <!-- Specify the columns to ensure that their order is correct. -->
        <mx:columns>
            <mx:DataGridColumn dataField="Index" />
            <mx:DataGridColumn dataField="Qty" />
        </mx:columns>
    </mx:PrintDataGrid>
    
    <!-- Create a FormPrintFooter control 
        and set its prodTotal variable. -->
    <MyComp:FormPrintFooter id="footer" pTotal="{prodTotal}"/>
</mx:VBox>

Header and footer files

The following lines show the FormPrintHeader.mxml file.

<?xml version="1.0"?>
<!-- printing\myComponents\FormPrintHeader.mxml -->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" 
    width="60%"
    horizontalAlign="right" >

    <mx:Label text="This is a placeholder for first page contents"/>
</mx:VBox>

The following lines show the FormPrintFooter.mxml file:

<?xml version="1.0"?>
<!-- printing\myComponents\FormPrintFooter.mxml -->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" 
    width="60%"
    horizontalAlign="right">
    
    <!-- Declare and initialize the product total variable. -->
    <mx:Script>
        <![CDATA[
            [Bindable]
            public var pTotal:Number = 0;
        ]]>
        </mx:Script>

    <mx:Label text="Product Total: {pTotal}"/>
</mx:VBox>

Using the PrintAdvancedDataGrid control

The PrintAdvancedDataGrid and PrintOLAPDataGrid controls provide the same functionality for the AdvancedDataGrid and OLAPDataGrid controls as the PrintDataGrid control does for the DataGrid control. For more information, see Using the PrintDataGrid control for multipage grids.

The following example uses the PrintAdvancedDataGrid control to print an instance of the AdvancedDataGrid control.

<?xml version="1.0"?>
<!-- printing\ADGPrint.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

    <mx:Script>
        <![CDATA[
            import mx.printing.*;
            import mx.collections.ArrayCollection;
            import mx.printing.PrintAdvancedDataGrid;
                    
            include "SimpleHierarchicalData.as";

            // Create a PrintJob instance.
            private function doPrint():void {
                // Create an instance of the FlexPrintJob class.
                var printJob:FlexPrintJob = new FlexPrintJob();
                
                // Initialize the PrintAdvancedDataGrid control.
                var printADG:PrintAdvancedDataGrid = 
                    new PrintAdvancedDataGrid();
                // Exclude the PrintAdvancedDataGrid control from layout.
                printADG.includeInLayout = false;
                printADG.source = adg;

                // Add the print-specific control to the application.                
                addChild(printADG);
                
                // Start the print job.
                if (printJob.start() == false) {                
                    // User cancelled print job.
                    // Remove the print-specific control to free memory.                
                    removeChild(printADG);
                    return;
                }

                // Add the object to print. Do not scale it.
                printJob.addObject(printADG, FlexPrintJobScaleType.NONE);

                // Send the job to the printer.
                printJob.send();

                // Remove the print-specific control to free memory.                
                removeChild(printADG);
            }
        ]]>
    </mx:Script>

    <mx:VBox id="myVBox"
        width="100%" height="100%">
        <mx:AdvancedDataGrid id="adg"
            width="100%" height="100%">
            <mx:dataProvider>
                <mx:HierarchicalData source="{dpHierarchy}"/>
            </mx:dataProvider>
            <mx:columns>
                <mx:AdvancedDataGridColumn dataField="Region"/>
                <mx:AdvancedDataGridColumn dataField="Territory_Rep"
                    headerText="Territory Rep"/>
                <mx:AdvancedDataGridColumn dataField="Actual"/>
                <mx:AdvancedDataGridColumn dataField="Estimate"/>
            </mx:columns>
        </mx:AdvancedDataGrid>    

        <mx:Button id="myButton" 
            label="Print" 
            click="doPrint();"/>
    </mx:VBox>    
</mx:Application>

The executing SWF file for the previous example is shown below:

 

This example uses the PrintAdvancedDataGrid.source property to initialize the PrintAdvancedDataGrid control from the AdvancedDataGrid control.

To support the AdvancedDataGrid control, the PrintAdvancedDataGrid control adds the following properties not available in the PrintDataGrid control:

Property

Description

allowInteractions

If true, allow some interactions with the control, such as column resizing, column reordering, and expanding or collapsing nodes. The default value is false.

displayIcons

If true, display the folder and leaf icons in the navigation tree. The default value is true.

source

Initialize the PrintAdvancedDataGrid control and all of its properties from the specified AdvancedDataGrid control.

validPreviousPage

Indicates that the data provider contains data rows that precede the rows that the PrintAdvancedDataGrid control currently displays.

 



Posted by 1010
반응형

PrintDataGrid,percentHeight and multipage layouts

Categories: actionscript, flex
Written By: sebi

I rarely had to create any complex layouts for printing with Flex, until now.
The layout has to have different pages, with different elements on them. The issue is that the PrintDataGrid calculated it’s height right (well, almost) at the first page, but maintained this value to the rest of the pages – instead of recalculating based on that structures.

There is a chapter in the Flex help discussing this scenario: Using the PrintDataGrid control for multipage grids.

I implemented the whole print layout view based on this description, and quickly realized that even the result is not exactly what I wanted.

Print result

Print result

The help suggest something that sounds relevant to this issue, but it didn’t solve my problem.

When you use a PrintDataGrid control to print a single data grid across multiple pages, you queue each page of the grid individually. If your application customizes each page beyond simply using the nextPage() method to page through the PrintDataGrid, you must call the validateNow() method to update the page layout before you print each page, as shown in Print output component.

The root of the problem is that the PrintDataGrid’s setActualSize contains an interesting line, which resets the percentHeight of the grid to NaN. As a result, the parent container of the grid won’t resize the grid, even if it’s layout changes, and we call validateNow().

	/**
	 *  @private
	 *  setActualSize() is overridden to update _originalHeight.
	 */
	override public function setActualSize(w:Number, h:Number):void
	{
		if (!isNaN(percentHeight))
		{
			_originalHeight = h;
			super.percentHeight = NaN;
			measure();
			h = measuredHeight;
		}
 
		super.setActualSize(w, h);
 
		invalidateDisplayList();
 
		if (sizeToPage && !isNaN(explicitHeight))
			explicitHeight = NaN;
	}

The whole resizing cycle of the grid seemed to be quite complex, so I decided to look for an easier solution than trying to extend-override some of it’s steps, so ended up with the following:

	public function showPage( pageType:String ):void 
	{
		switch( pageType )
		{
			case "first":
			case "single":
				meetingInfoExtended.visible 		= true;
				meetingInfoExtended.includeInLayout	= true;
				meetingInfo.visible 		= false;
				meetingInfo.includeInLayout= false;						
				break;
			case "middle":
			case "last":
				meetingInfoExtended.visible 		= false;
				meetingInfoExtended.includeInLayout	= false;
				meetingInfo.visible 		= true;
				meetingInfo.includeInLayout= true;						
				break; 
		}
		printingGrid.percentHeight = 100;
		validateNow();
	}

Basically re-apply the percentHeight setting in every paging, forcing the grid to recalculate it’s size.

There was another small glitch, what caused a scrollbar to appear in the print layout – at least it tried to appear. I have a multi-line text there, an mx:Text, and I think the text couldn’t calculate it height fast enough, but if I didn’t pass a fixed height to it, the grid miss-calculated it’s height. But only the text caused this problem with the percent heights, other components could handle it.

One Response to “PrintDataGrid,percentHeight and multipage layouts”

  1. Robert Cesaric Says:

    Thanks for post.. We were just building a print view that head custom header/footer and had the same issue. Your percentHeight fix worked perfectly.

    We’re still noticing issues priting with the datagrid variableRowHeight but I’m much happier getting this bug fixed first. Thx!

 

Posted by 1010
반응형

How to Export into PDF/ / Flex + PDF / Generate PDF From Flex

To generate PDF files from Flex all you need is some external liberies like AlivePDF.swc or generatePdf.swc.
Im my Example i use Alive PDF as its Free and easy to use.
Get AlivePDF.swc
use following save structure to add images or Ui Container to add in PDF file
Get AlivePDF Here : AlivePDF
protected function savePDF():void
{
var myPDFEncoder:PDF = new PDF (Orientation.LANDSCAPE, Unit.MM);
myPDFEncoder.setDisplayMode(Display.FULL_PAGE);
myPDFEncoder.addPage();
myPDFEncoder.addImage(ChartDetails,null,0,0,myPDFEncoder.getMargins().width,myPDFEncoder.getMargins().height);

var bytes:ByteArray = myPDFEncoder.save(Method.LOCAL);
var fx:FileReference = new FileReference();
fx.save(bytes,"ABC.pdf");
}


Posted by 1010
반응형

Save file as PDF in flex

By in flex, Usability

Saving content as PDF from flex had always been a hassle, We know that doing such file operations in AIR is much simpler but due to the file saving ability added in Flash player 10 One can easily save files to the local system.

The next big question is how do i convert my content to PDF format so that it can be correctly saved into the file. Well for that folks at AlivePDF have done a gr8 job and now any one can encode their content into pdf and offer for download. They have a good documentation section which you can read and create more complex stuff.

For beginners i am writing a small example in which i will let the user download a PDF with embedded text or the Image depending on his choice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<mx:application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="522">
<mx:script>
    < ![CDATA[
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.text.*;
    import flash.utils.ByteArray;

    import mx.graphics.codec.JPEGEncoder;

    import org.alivepdf.layout.Orientation;
    import org.alivepdf.layout.Size;
    import org.alivepdf.layout.Unit;
    import org.alivepdf.pdf.PDF;
    import org.alivepdf.saving.Method;

    protected var fileRefPDF:PDF;

    private function onSaveClicked(e:Event):void
    {
    fileRefPDF = new PDF(Orientation.PORTRAIT, Unit.MM, Size.LETTER);
    fileRefPDF.addPage();

    if(check.selected){
    fileRefPDF.addImage(pics);
    fileRefPDF.addMultiCell(200,5,myname.text);
    }else{
    fileRefPDF.addMultiCell(200,5,myname.text);
    }

    var bytes:ByteArray = fileRefPDF.save(Method.LOCAL);
    var file:FileReference = new FileReference();
    file.save(bytes,"Untitled.pdf");
    }



    ]]>
</mx:script>

    <mx:canvas id="pics">
        <mx:image source="@Embed('hello.jpg')" x="198" y="19" width="311" height="208"/>
        <mx:label text="Oh!! Hi," x="330" y="166"/>
        <mx:label text="{myname.text}" x="330" y="184"/>
    </mx:canvas>



    <mx:label top="33" left="14" text="Save as PDF Test "  fontWeight="bold" fontSize="15"/>    
    <mx:button click="onSaveClicked(event)" label="Save to PDF" id="saveBtn" x="14" y="148"/>
    <mx:label text="Enter your name:" y="72" x="14"/>
    <mx:textinput id="myname"  x="14" y="90"/>
    <mx:checkbox id="check" label="Save image" selected="false"  x="14" y="121"/>
</mx:application>

the Screenshot of the same can be seen below.

Note: please include the AlivePDF swc in your project.

I have just skimmed the surface of the ocean with the above code. but you can write your own brew of concoction that could possibly bring WORLD PEACE :)

(-_-) happy loafing !

 

Posted by 1010
반응형

Toggling draggable columns in a Flex DataGrid control

By On August 30, 2007 · Leave a Comment

The following example demonstrates how you can enable/disable draggable columns in a Flex DataGrid control using the DataGrid class’s draggableColumns property, as well as toggling specific column’s dragability using the DataGridColumn class’s draggable property.

Full code after the jump.

View MXML

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2007/08/30/toggling-draggable-columns-in-a-flex-datagrid-control/ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white">

    <mx:Array id="arr">
        <mx:Object label="User 1" data="1" />
        <mx:Object label="User 2" data="2" />
        <mx:Object label="User 3" data="3" />
        <mx:Object label="User 4" data="4" />
        <mx:Object label="User 5" data="5" />
        <mx:Object label="User 6" data="6" />
        <mx:Object label="User 7" data="7" />
        <mx:Object label="User 8" data="8" />
    </mx:Array>

    <mx:ApplicationControlBar dock="true">
        <mx:CheckBox id="draggableColumnsCh"
                label="draggableColumns"
                selected="true" />

        <mx:CheckBox id="labelDraggableCh"
                label="label.draggable"
                selected="true" />

        <mx:CheckBox id="dataDraggableCh"
                label="data.draggable"
                selected="true" />
    </mx:ApplicationControlBar>

    <mx:DataGrid id="dataGrid"
            draggableColumns="{draggableColumnsCh.selected}"
            dataProvider="{arr}">
        <mx:columns>
            <mx:DataGridColumn dataField="label"
                    draggable="{labelDraggableCh.selected}" />
            <mx:DataGridColumn dataField="data"
                    draggable="{dataDraggableCh.selected}" />
        </mx:columns>
    </mx:DataGrid>

</mx:Application>

View source is enabled in the following example.

If you want to detect when a data grid column has been reordered, you can listen for the headerShift event on the DataGrid control, as seen in the following example:

View MXML

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2007/08/30/toggling-draggable-columns-in-a-flex-datagrid-control/ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="horizontal"
        verticalAlign="middle"
        backgroundColor="white">

    <mx:Script>
        <![CDATA[
            import mx.controls.dataGridClasses.DataGridColumn;
            import mx.controls.DataGrid;
            import mx.events.IndexChangedEvent;

            private function dataGrid_headerShift(evt:IndexChangedEvent):void {
                var dg:DataGrid = DataGrid(evt.currentTarget);
                var column:DataGridColumn = dg.columns[evt.newIndex];
                debug.text += column.dataField + " (oldIndex:" + evt.oldIndex + ", newIndex:" + evt.newIndex + ")" + "\\n";
            }
        ]]>
    </mx:Script>

    <mx:Array id="arr">
        <mx:Object label="User 1" data="1" />
        <mx:Object label="User 2" data="2" />
        <mx:Object label="User 3" data="3" />
        <mx:Object label="User 4" data="4" />
        <mx:Object label="User 5" data="5" />
        <mx:Object label="User 6" data="6" />
        <mx:Object label="User 7" data="7" />
        <mx:Object label="User 8" data="8" />
    </mx:Array>

    <mx:DataGrid id="dataGrid"
            dataProvider="{arr}"
            headerShift="dataGrid_headerShift(event)">
        <mx:columns>
            <mx:DataGridColumn dataField="label" />
            <mx:DataGridColumn dataField="data" />
        </mx:columns>
    </mx:DataGrid>

    <mx:TextArea id="debug"
            width="{dataGrid.width}"
            height="{dataGrid.height}" />

</mx:Application>

View source is enabled in the following example.

 

Posted by 1010
반응형

Dragging rows between two different Flex DataGrid controls

By On September 19, 2007 · 42 Comments

The following example shows how you can use the dragEnabled, dropEnabled, and dragMoveEnabled properties with the Flex DataGrid control to copy and move rows between different data grids.

Full code after the jump.

View MXML

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2007/09/19/dragging-rows-between-two-different-flex-datagrid-controls/ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="horizontal"
        verticalAlign="middle"
        backgroundColor="white">

    <mx:Array id="arr">
        <mx:Object colA="Item A.0" colB="Item B.0" colC="Item C.0" />
        <mx:Object colA="Item A.1" colB="Item B.1" colC="Item C.1" />
        <mx:Object colA="Item A.2" colB="Item B.2" colC="Item C.2" />
        <mx:Object colA="Item A.3" colB="Item B.3" colC="Item C.3" />
        <mx:Object colA="Item A.4" colB="Item B.4" colC="Item C.4" />
        <mx:Object colA="Item A.5" colB="Item B.5" colC="Item C.5" />
        <mx:Object colA="Item A.6" colB="Item B.6" colC="Item C.6" />
        <mx:Object colA="Item A.7" colB="Item B.7" colC="Item C.7" />
        <mx:Object colA="Item A.8" colB="Item B.8" colC="Item C.8" />
        <mx:Object colA="Item A.9" colB="Item B.9" colC="Item C.9" />
    </mx:Array>

    <mx:ApplicationControlBar dock="true">
        <mx:Form>
            <mx:FormItem label="DataGrid #1:"
                    direction="horizontal">
                <mx:CheckBox id="dg1_dragEnabled"
                        label="dragEnabled" />
                <mx:CheckBox id="dg1_dropEnabled"
                        label="dropEnabled" />
                <mx:CheckBox id="dg1_dragMoveEnabled"
                        label="dragMoveEnabled" />
            </mx:FormItem>
            <mx:FormItem label="DataGrid #2:"
                    direction="horizontal">
                <mx:CheckBox id="dg2_dragEnabled"
                        label="dragEnabled" />
                <mx:CheckBox id="dg2_dropEnabled"
                        label="dropEnabled" />
                <mx:CheckBox id="dg2_dragMoveEnabled"
                        label="dragMoveEnabled" />
            </mx:FormItem>
        </mx:Form>
    </mx:ApplicationControlBar>

    <mx:VBox width="50%">
        <mx:Label text="DataGrid #1" />
        <mx:DataGrid id="dataGrid1"
                width="100%"
                rowHeight="22"
                dataProvider="{arr}"
                dragEnabled="{dg1_dragEnabled.selected}"
                dragMoveEnabled="{dg1_dragMoveEnabled.selected}"
                dropEnabled="{dg1_dropEnabled.selected}"
                verticalScrollPolicy="on">
            <mx:columns>
                <mx:DataGridColumn dataField="colA"
                        headerText="Column A" />
                <mx:DataGridColumn dataField="colB"
                        headerText="Column B" />
                <mx:DataGridColumn dataField="colC"
                        headerText="Column C" />
            </mx:columns>
        </mx:DataGrid>
        <mx:Label text="{dataGrid1.dataProvider.length} items" />
    </mx:VBox>

    <mx:VBox width="50%">
        <mx:Label text="DataGrid #2" />
        <mx:DataGrid id="dataGrid2"
                width="100%"
                rowHeight="22"
                dataProvider="[]"
                dragEnabled="{dg2_dragEnabled.selected}"
                dragMoveEnabled="{dg2_dragMoveEnabled.selected}"
                dropEnabled="{dg2_dropEnabled.selected}"
                verticalScrollPolicy="on">
            <mx:columns>
                <mx:DataGridColumn dataField="colA"
                        headerText="Column A" />
                <mx:DataGridColumn dataField="colB"
                        headerText="Column B" />
                <mx:DataGridColumn dataField="colC"
                        headerText="Column C" />
            </mx:columns>
        </mx:DataGrid>
        <mx:Label text="{dataGrid2.dataProvider.length} items" />
    </mx:VBox>

</mx:Application>

View source is enabled in the following example.

Posted by 1010
반응형

출처 : http://wonsama.tistory.com/211

 

0. 참조 문서
blazeDS 개발자 가이드 : http://livedocs.adobe.com/blazeds/1/blazeds_devguide/

blazeDS 다운로드 : http://opensource.adobe.com/wiki/display/blazeds/download+blazeds+3



 

1. blazeds.war 파일 다운로드 이후 Eclipse or Tomcat Import



 

2. 중요 참조 [jre 5.0 이상에서만 동작함에 유의 !]



blazeds.war 파일을 초기 import 하면 WEB-INF 폴더가 생성됩니다.

WEB-INF
└flex
  └ services-config.xml : 전반적인 서비스 설정을 포함.
  └ messaging-config.xml : Message Push 서비스 등 메시지 관련 서비스 설정을 포함.
  └ remoting-config.xml : Remote Object 호출 서비스 설정을 포함.
  └ proxy-config.xml : 프록시 서비스를 사용할 때
└lib - Flex에서 사용되는 library 파일이 존재하는 폴더
└web.xml - Flex Session Management, MessageBroker Servlet 설정에 대한 내용 포함.

3. services-config.xml



: 기본 채널의 설정 및 포함하는 서비스 파일의 경로를 확인 할 수 있다.
my-amf, my-secure-amf, my-polling-amf ..

4. 나머지 .xml 파일



- 설정된 해당 서비스를 확인할 수 있다.
- destination id 설정을 통해 flex에서 접근할 수 있도록 설정한다.

ex)

ㄱ. MessagePush : 해당 채널에 대한 종착점 id를 설정한다.
< destination id="realMsg"/>

ㄴ. Remote Object : 해당 원격지 클래스를 호출하기 위한 종착점 id를 설정한다.
< destination id="fruitRO">
        <properties>
            <source>wonsama.test.FruitManager</source>
        </properties>
< /destination>

5. 기타 확인사항



ㄱ. <default-channels> 하위 노드에 여러개의 채널이 선언된 경우
맨 위쪽 부터 처리하며, 채널이 동작하지 않는 경우 다음 채널 서비스를 실행한다.

    <default-channels>
        <channel ref="my-streaming-amf"/> <!-- 처음 실행됨 -->
      <channel ref="my-polling-amf"/> <!-- 위 채널이 동작하지 않으면 동작 -->
    </default-channels>

ㄴ. StreamingAMFChannel 사용 시 <properties>의 하위노드가 1개 이상 존재해야 한다.
==> 존재하지 않으면 Explore에서 정상적으로 MessagePush가 동작하지 않음. (버그인듯?)

ex)
< channel-definition id="my-streaming-amf"
    class="mx.messaging.channels.StreamingAMFChannel">
    <endpoint
        url="http://{server.name}:{server.port}/{context.root}/messagebroker/streamingamf"
        class="flex.messaging.endpoints.StreamingAMFEndpoint" />
    <properties>
        <idle-timeout-minutes>0</idle-timeout-minutes>
        <max-streaming-clients>10</max-streaming-clients>
        <server-to-client-heartbeat-millis>5000    </server-to-client-heartbeat-millis>
        <user-agent-settings>
            <user-agent match-on="MSIE" kickstart-bytes="2048"
                        max-streaming-connections-per-session="3" />
            <user-agent match-on="Firefox" kickstart-bytes="2048"
                        max-streaming-connections-per-session="3" />
        </user-agent-settings>
    </properties>
< /channel-definition>

ㄷ. 익스플로러에서 모든 창을 종료하지 않으면 Messaging Service에서 Lock이 발생되어 실시간 메시지 수신을 할 수 없다(약 10초 ~ ???초). (MessageBroker쪽)

파폭에서는 발생하지 않지만 익스플로러에서는 위와 같은 문제가 발생하네여 아.... 초난감 ;;
24시간 시간 모니터링 툴을 제작시 반드시 고려를 하시기 바랍니다. 프로젝트 3개월 차에 이런 버그를 발견해 내서리 쩝...

고객사에는 뭐 끄지마세요 이런식으로 -_-; 아.... 중요 정보 인것 같아 이전 포스트에 덧칠하여 다시 올려 봅니다.

Posted by 1010
반응형

The DateFormatter class uses a format String to return a formatted date and time String from an input String or a Date object. The DateFormatter is a very simple formatter. To use it you just have to specify the formatString property, apply the format and you are done.
<mx:DateFormatter formatString="Y|M|D|A|E|H|J|K|L|N|S"/>
Here are some short explanations of what the characters in formatString represent:

Y – Year
M – month
D – Day
A – AM/PM indicator
E – day of the week
H – Hour (1-24 hours format)
J – Hour (0-23 hours format)
K – Hour in am/pm (0-11 hours format)
L – Hour in am/pm (1-12 hours format)
N – Minutes
S – Seconds
The formatString may also contain other words as separators between Y, M, D, A etc. as we will see in the example below.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical
   backgroundColor="white" 
    creationComplete="formatDates()" viewSourceURL="srcview/index.html">
    
    <mx:Script>
        <![CDATA[
            private function formatDates():void
            {
                dateFormatter.formatString = "DD/MM/YY";
                formattedDate1.text = dateFormatter.format(initialDate.text);
                dateFormatter.formatString = "EEE DD/MM/YY";
                formattedDate2.text = dateFormatter.format(initialDate.text);
                dateFormatter.formatString = "EEEE DD/MM/YY";
                formattedDate3.text = dateFormatter.format(initialDate.text);
                dateFormatter.formatString = "EEEE DD MMM YYYY";
                formattedDate4.text = dateFormatter.format(initialDate.text);
                dateFormatter.formatString = "EEEE DD MMMM YYYY";
                formattedDate5.text = dateFormatter.format(initialDate.text);
                dateFormatter.formatString = "EEEE DD MMMM YYYY at HH:NN";
                formattedDate6.text = dateFormatter.format(initialDate.text);
                dateFormatter.formatString = "EEEE DD MMMM YYYY at HH:NN A";
                formattedDate7.text = dateFormatter.format(initialDate.text);
                dateFormatter.formatString = "EEEE DD MMMM YYYY at HH:NN.SS A";
                formattedDate8.text = dateFormatter.format(initialDate.text);
            }
        ]]>
    </mx:Script>
    
    <mx:DateFormatter id="dateFormatter" />
    <mx:Text text="Change The Initial Date And Format The New Dates
       fontWeight="bold"/>    
    <mx:Form>
        <mx:FormItem label="Initial Date:" >
            <mx:TextInput id="initialDate" text="01.01.2009 10:34 57" />
        </mx:FormItem>
        <mx:FormItem label="DD/MM/YY" >
            <mx:TextInput id="formattedDate1" />
        </mx:FormItem>
        <mx:FormItem label="EEE DD/MM/YY" >
            <mx:TextInput id="formattedDate2" />
        </mx:FormItem>
        <mx:FormItem label="EEEE DD/MM/YY" >
            <mx:TextInput id="formattedDate3" />
        </mx:FormItem>
        <mx:FormItem label="EEEE DD MMM YYYY" >
            <mx:TextInput id="formattedDate4" />
        </mx:FormItem>
        <mx:FormItem label="EEEE DD MMMM YYYY" >
            <mx:TextInput id="formattedDate5" />
        </mx:FormItem>
        <mx:FormItem label="EEEE DD MMMM YYYY at HH:NN" >
            <mx:TextInput id="formattedDate6" />
        </mx:FormItem>
        <mx:FormItem label="EEEE DD MMMM YYYY at HH:NN A" >
            <mx:TextInput id="formattedDate7" />
        </mx:FormItem>
        <mx:FormItem label="EEEE DD MMMM YYYY at HH:NN.SS A" >
            <mx:TextInput id="formattedDate8" />
        </mx:FormItem>
        <mx:FormItem >
            <mx:Button click="formatDates()" label="format dates"/>
        </mx:FormItem>
    </mx:Form></mx:Application>

 

Posted by 1010
반응형

Integrating Adobe Flex and IBM WebSphere Portal

Xiang Cheng (xcheng@cn.ibm.com), Software Engineer, IBM China
Zhi Hao Zhang (zhangzh@cn.ibm.com), Software Engineer, IBM China
Jia Gu (gujia@cn.ibm.com), Software Engineer, IBM China

Summary:  Adobe® Flex takes you to the next level of Web application development with the concept of Rich Internet Applications (RIAs), while IBM® WebSphere® Portal provides a composite tooling to build flexible, SOA-based solutions. But how do you get the two of them together? One option is to directly integrate Flex into WebSphere Portal server. This article walks you through a process to quickly build rich client and component-based Flex applications for WebSphere Portal, as well as a helpful method to reduce the size of WAR files.

Date:  12 May 2009
Level:  Introductory PDF:  A4 and Letter (520KB | 21 pages)Get Adobe® Reader®
Also available in:   Chinese

Activity:  19673 views
Comments:   3 (View | Add comment - Sign in)

Average rating 3 stars based on 12 votes Average rating (12 votes)
Rate this article

Prerequisites

This article is for Flex developers who want to integrate their applications with WebSphere Portal. It assumes that you are familiar with Flex at a basic programming level and that you are familiar with Java™ programming. It also assumes that you have administrative access to a working WebSphere Portal server for the relevant portions of the article. It does not, however, assume that you are familiar with programming or administering WebSphere Portal.

Along with this article, you will need the following tools installed to make sure the sample case works well.

  • Adobe Flex Builder — This article is written with Adobe Flex Builder 3. Of course, you can do it with the appropriate JDK and a text editor, but the amount of extra work is significant.
  • WebSphere Portal V6.0.1 or higher — If you are using WebSphere Portal V6.0, you should update your WebSphere Portal V6 environment. (See Resources.)
  • IBM Rational Software Architecture v7.0.0
  • IBM DB2 Enterprise v9.1

Overview of sample application

Let's briefly look at the business requirements of this sample application called TODOList. Many users want to keep records for their appointments, anniversaries, reminders, or any events. And they need to conveniently view their coming items, create new items, and delete any items if possible. Can we provide a smart tool to help them? In this article, we will show you how to build the application in a process that is similar to the way most developers create applications in order to illustrate the complete development procedures.

Figure 1 below shows the sample application infrastructure. We will quickly state the technical aspects of the sample before building the actual application with Adobe Flex and IBM WebSphere Portal. The whole application will be built in a WAR file, including presentation layer and business layer modules, although they are developed in the different projects.

The top rectangle represents a Flex project as the presentation layer that is built by MXML and ActionScript, while the bottom rectangle represents a Java project as the business layer that is built by Java and JDBC. The Flex application calls a Java service through a RemoteObject that is one of the remote procedure call (RPC) components provided by Flex. In this sample application, we will import BlazeDS to implement the remote object.


Figure 1. Infrastructure
A block diagram shows the relationships between the various components within WebSphere Portal.  The Presentation Layer is shown at the top with the Business Layer in the bottom, connected together through BlazeDS(remote object.)  MXML and AS are shown in the Presentation Layer.  Services and JDBC are shown in the Business layer.  A further connection goes to DB2 outside of the business layer from JDBC.

Build Flex project

Adobe Flex is a highly productive, free, open source framework for building and maintaining expressive Web applications that deploy consistently on all major browsers, desktops, and operating systems.

It consists of a rich component library with more than 100 proven, extensible UI components for creating RIAs. It also provides a standard-based language and programming model that supports common design patterns. MXML, a declarative XML-based language, is used to represent UI layout and behaviors, and each MXML file is a separate component. ActionScript, a powerful object-oriented programming language, is used to create client logic. ActionScript provides flow control and object manipulation features that are not available in MXML. Flex 3 was released at the end February 2008, and you can learn more about Flex 3 features from the Adobe Flex Web site. (See Resources.)

Flex applications can be built only by the free Flex SDK, which comprises the Flex framework (component class library) and Flex compiler, enabling you to freely develop and deploy Flex applications using an IDE of your choice. Developers can use Adobe Flex Builder 3 to dramatically accelerate development.

The Flex wizard guides you on how to quickly and easily create this sample in Flex Builder 3.

  1. Open Flex Builder 3, and select File --> New --> Flex Project.
  2. Enter TODOListFlex as the project name.
  3. Select the default location, which is the workspace, as the project location.
  4. Choose Web application as the application type.
  5. For the application server type, you may follow the steps in Adobe Flex 3 Help\Using Flex Builder 3\Flex Builder Basic\Working With Projects if you have a server. But in this sample, we select None, as the Figure 2 screen shot shows, because we don't need that.

Figure 2. New Flex project
A file tree shows the contents of TODOListFlex, including the folders bin-debug, html-template, libs and src.  Src is further expanded to show a folder image containing the file icon-delete.gif, a folder model containing the file TodoItemVBean.as, the folder util containing the file TokenResponder.as, and the file TODOListFlex.mxml.
  1. Keep all default settings for this Flex application and the build paths in the next two configuration pages.
  2. Click Finish to create this project.

Then, Flex Builder 3 will generate a primary MXML with the same name of new project. This application includes a MXML file, which starts with the <mx:Application> root tag, as follows in Listing 1.


Listing 1. MXML application root
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
</mx:Application>
            

In this section, you only need to care about the main file, TODOListFlex.mxml. You can use the Flex navigator view to import other resources (MXML, ActionScript, and Image) into the project from our sample code. A view of the project category is shown below in Figure 3.


Figure 3. Flex project category
A file tree shows the contents of TODOListFlex, including the folders bin-debug, html-template, libs and src.  Src is further expanded to show a folder image containing the file icon-delete.gif, a folder model containing the file TodoItemVBean.as, the folder util containing the file TokenResponder.as, and the file TODOListFlex.mxml.

The MXML editor in Flex Builder 3 allows you to work in either Source or Design mode at the top of the editor area. Make sure that you are in Source mode, and then add a <mx:Panel> component as the child of the main application to represent UI layout, and place a <mx:VBox> component in this panel to make it lay in vertical. See Listing 2.


Listing 2. Add panel and VBox in MXML file
<mx:Panel title="TODO List" width="100%" height="100%" 
   paddingLeft="4" paddingTop="8" paddingRight="8" paddingBottom="4">
   <mx:VBox width="100%">
   </mx:VBox>
</mx:Panel>

Put a <mx:DataGrid> component between <mx:VBox> tags to list all to-do items. We define an ArrayCollection todoItemColl and assign it as the dataProvider for the DataGrid. (See Listing 3.) We will call RemoteObject service to obtain data for this ArrayCollection in the section, "Use RemoteObject in Flex."


Listing 3. Add DataGrid in VBox
<mx:DataGrid id="todoListDG" dataProvider="{todoItemColl}">
   <mx:columns>
   <mx:DataGridColumn visible="false" dataField="id"/>
   <mx:DataGridColumn width="100" headerText="Date" dataField="deadlineStr"/>
   <mx:DataGridColumn width="400" headerText="Subject" dataField="task"/>
   ...
   </mx:columns>
</mx:DataGrid>

There is a form under the VBox to allow users to add new to-do items. The form contains three text fields (such as Date, Subject, and Owner) and a button. We use a <mx:Grid> component here to control the UI alignment. (See Listing 4.)


Listing 4. Add grid for item form
<mx:Grid>
   <mx:GridRow>
      <mx:GridItem> 
         <mx:Label text="Date:"/>
      </mx:GridItem>
      <mx:GridItem>
         <mx:DateField id="deadlineDateField" formatString="YYYY-MM-DD"/>
      </mx:GridItem>
   </mx:GridRow>
   ...
   <mx:GridRow>
      <mx:GridItem colSpan="2" horizontalAlign="right">
         <mx:Button label="Add" click="saveNewItem()"/>
      </mx:GridItem>
   </mx:GridRow>
</mx:Grid>

Then we create client logic with ActionScript codes within a <mx:Script> tag and wrap the contents in a CDATA construct. (See Listing 5.) To access the different data sources, we define a constant, USE_MOCK_DATA, to control whether the application accesses remote data or local mock data. Now we set the constant value to be true because we are just showing it as a separate application for now. We will elaborate on how to use RemoteObject Service to assess data in the section, "Use RemoteObject in Flex."


Listing 5. Add script to call data source
<mx:Script>
   <![CDATA[
      …
      private static const USE_MOCK_DATA:Boolean = true;	…
      private function getTodoList():void
      {
         if(USE_MOCK_DATA) {
            // Creat the mock data
         }
         else {
            // Use remote object to access the back-service
         }
      }
      …
   ]]>
</mx:Script>

You will find more sample code in the attachment. (See Downloads.) After the above steps, select TODOListFlex.mxml and click the Run button at the toolbar. Flex Builder will compile this application and show the below pages, as in Figure 4.


Figure 4. TODOList Page
A screen shot of the TODOList Page showing a title, TODO List at the top.  Below that is a label which reads Total 3 TODO item(s).  Beneath the label is a table with four columns labeled from left to right Date, Subject, Owner.  The fourth columns is unlabeled.  Next follow three sample Todos with a date in column 1, a description in column 2, a person's name in column 3 and a checked box in the fourth column.  Under the table are entry fields for Date, Subject and Owner with an Add button.

Build portal project

WebSphere Portal is a framework that lets you plug in new features or extensions called portlets. Portlets are applications within WebSphere Portal, which are the encapsulation of reusable components that combine Web-based content, application functionality, and access to resources.

WebSphere Portal, starting with Version 5.0.2.1, provides a runtime environment for both JSR 168 Portlet API and IBM Portlet API. JSR 168 is a specification from the Java Community Process for the standardization of portlets. The specification was developed to provide interoperability for running portlets on any vendor's implementation of the JSR 168 portlet container. The IBM Portlet API is now deprecated in WebSphere Portal V6.0 in order to show IBM's commitment to the JSR 168 standard portlet API. Also, many new functions will only be available to standard portlets. We will use the JSR 168 standard portlet in our sample.

This section shows how to create a new portlet project using the Rational Software Architecture (RSA) wizard. (See Figure 5.)

  1. Open RSA, and select File --> New --> Project.
  2. Select Portlet Project to create a project.
  3. Enter TODOList as the project name.
  4. Target runtime should be WebSphere Portal V6.0 stub.
  5. Portlet API should be JSR 168 Portlet.
  6. Select the checkbox of "Create a portlet".
  7. Make sure the Basic Portlet is selected as Portlet type.
  8. Click Next, leaving all fields as default.
  9. Click Finish to create the portlet project.

Figure 5. New portlet project
New Portlet Project window with the following settings: Project name is "TODOList;" Project contents has "use default" checked; Target Runtime is "WebSphere Portal v6.0 stub;" EAR Membership has "Add project to an EAR" checked and EAR Project Name set to "TODOListEAR;" Portlet API is "JSR 168 Portlet;" Create a portlet is checked; Portlet name is "TODOList;" Portlet type is "Basic Portlet;" Show advanced settings is checked.

After you finish the above steps, the project category should look like Figure 6.


Figure 6. Portlet project category
The file structure of the New Portlet Project is shown with a root of TODOList with the skeletal structure and components created.

Actually, you can deploy this portlet on your portal server now. But we will integrate the Flex application created in the previous section into the portlet project first.


Integrate Flex into the portlet project

To integrate the Flex application into our portlet project we must complete a few steps.

Introducing Web-tier compiler

The Flex compiler creates SWF files that run in Adobe Flash Player. Flex offers several kinds of compilers: the Web-tier compiler, the mxmlc command-line compiler, and the Flex Builder project compiler. In this article, we will use the Web-tier compiler, which is a set of servlets and servlet filters that run in a J2EE application server. The application server passes on requests from *.mxml files to the servlet container, which then compiles into a SWF file and returns the results to the client. In this case, it allows you to simply copy the Flex files into a directory visible to your server, and the flex codes will be compiled automatically when you request the main application file using your Web browser. This also allows you to rapidly compile, test, and deploy an application instead of compiling your MXML files into a SWF file and then deploying its wrapper files on a Web server.

You should download the FlexModule_J2ER from the Adobe open source Web site if you have not already. Do the following steps to introduce it into this sample portlet project.

  1. Unzip the webtier.war file to a directory called webtier.
  2. Copy flex-bootstrap.jar and flex-bootstrap-jsp.jar from the webtier\WEB-INF\lib to the corresponding WEB-INF\lib directory of the portlet project.
  3. Copy all files and directories from the webtier\WEB-INF\flex directory to the corresponding WEB-INF\flex directory of the portlet project.
  4. Update web.xml file content in the WEB-INF\ directory of the portlet project according to the web.xml file in the webtier\WEB-INF (You can copy the web.xml from the sample code which we have updated accordingly -- see Downloads).

Introducing BlazeDS

The Flex SDK contains features for accessing server-side data. These components use RPC to interact with server environments to provide data to Flex applications and send data to back-end data sources.

Three kinds of RPC components are supported by Flex: HTTPService, WebService, and RemoteObject. A client-side RPC component calls a remote service and then stores the response data in an ActionScript object where you can easily obtain the data.

RemoteObject

We use the RemoteObject component in this sample because it lets you access business logic directly in its native format rather than formatting it as XML, as you do with HTTPService or WebService. This saves you the time required to expose existing logic as XML. The Flex application can access a Java object directly by remote invocation of a method on a designated object. Those objects on the server can then deal with its native data types as arguments, query a database from those arguments, and return its native data types as values.

Another benefit of RemoteObject services is the speed of communication across the wire. Data exchanges still happen over HTTP or HTTPS, but the data itself is serialized into the Action Message Format (AMF), which is a binary format for data serialization/deserialization and remote method invocation. It improves performance by dramatically compressing the size of data transferred and parsing binary data into objects in memory far more efficiently than parsing XML data.

Using BlazeDS

BlazeDS is an Adobe open source project that employs RemoteObject component to access remote Java objects. It contains configurable channels that transport the data between the client and server and can run on a J2EE application server.

You can download the BladeDS package from the Adobe open source Web site. (See Resources.) Do the following steps to enable this portlet application to use BlazeDS:

  1. Unzip the blazeds.war to a directory called blazeds.
  2. Copy all jar files from blazeds\WEB-INF\lib to the WEB-INF\lib directory.
  3. Copy all configuration files from the blazeds\WEB-INF\flex directory to the WEB-INF\flex directory of this application. Overwrite if there are existing ones.
  4. Define MessageBrokerServlet and a session listener in WEB-INF\web.xml of this portlet application, as below in Listing 6. You can skip this step if you copied the web.xml from our sample code.

Listing 6. Define MessageBrokerSerlet and session listener
<!-- Http Flex Session attribute and binding listener support -->
<listener>
   <listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>
	
<!-- MessageBroker Servlet -->
<servlet>
   <servlet-name>MessageBrokerServlet</servlet-name>
   <display-name>MessageBrokerServlet</display-name>
   <servlet-class> flex.messaging.MessageBrokerServlet </servlet-class>

   <init-param>
      <param-name>services.configuration.file</param-name>
      <param-value>/WEB-INF/flex/services-config.xml</param-value>
   </init-param>

   <load-on-startup>1</load-on-startup>
</servlet>
         

Copy Flex code into the portlet project

Copy all files under the src directory from the Flex project to the directory, WebContent/_TODOList/jsp/html. The category of the portlet project should look like Figure 7.


Figure 7. Copy Flex code into portlet
The file structure of the TODOList project is shown.  Under the WebContent, _TODOList, jsp, html section are the files TODOListFlex.mxml and TODOListPortletView.jsp.

Create Java RemoteObject class

For simplicity, we need only one Java RemoteObject class, TodoItemRO, as the remote business service. You can copy the Java source code and the jdbc.properties file from the sample code (WEB-INF\src) to the portlet project. TodoItem is the only entity Java bean in this sample. (See Figure 8.)


Figure 8. Java RemoteObject class
Diagram shows two boxes side by side.  On the left is the Java Class TodoItemBO with the contained components.  On the right is the Java Class containing TodoItem with its components.  A connecter labeled "use" runs TodoItemBO to TodoItem.

Configure RemoteObject in portal

Flex finds the remote services according to its configuration file, services-config.xml, in the WEB-INF\flex. You need to specify the fully qualified class name in the source property of a remoting service destination in the BlazeDS services-config.xml file, or a file that it includes by reference, such as the remoting-config.xml file. The class also must have a no-arg constructor which will be used by Flex to instantiate an instance. We configured the single Java RemoteObject in the remoting-config.xml file which is in WEB-INF\flex, as below in Listing 7.


Listing 7. Configure RemoteObject in portal
<destination id= ‘todoItemRO’>
   <properties>			 
      <source> todolist.ro.TodoItemRO </source>
   </properties>
</destination> 
            

Use RemoteObject in Flex

Now that we have created and configured the remote object, it's time to use it as a remote service.

First, create a RemoteObject component in TODOListFlex.mxml with a unique ID, srvTODOList. The value of the destination should match the destination entry in the WEB-INF\flex\remoting-config.xml file in the portal project. In this sample, it is todoItemRO. (See Listing 8.)


Listing 8. Create RemoteObject component in Flex
<mx:RemoteObject id="srvTODOList" destination="todoItemRO" showBusyCursor="true" 
requestTimeout="10"/>
           

Next, create an AsyncToken object to call the Java method defined in the destination service using the unique ID of the RemoteObject component. The AsyncToken class provides a place to set additional or token-level data for asynchronous RPC operations.

It also allows an IResponder to be attached for an individual call (or, callback method). We define a simple responder class, TokenResponder, that implements the IResponder Interface (see the code sample, TODOListFlex\src\util\TokenResponder.as). It will call the specified callback method when Java returns or raises a specified alert as the handler of any fault. We add it as the responder of the AsyncToken object, get the response data, and transform it into a Flex variable type. (See Listing 9.)


Listing 9. Create callback method in Flex
var asyncToken:AsyncToken = AsyncToken(srvTODOList.getTodoItems());
asyncToken.addResponder(new TokenResponder(displayTodoItems, "Error Getting TODO List"));

private function displayTodoItems(event:ResultEvent):void
{
   todoItemColl = event.result as ArrayCollection;
   todoItemColl.refresh();
}
           

Include Flex component in JSP

The Flex compiler module for J2EE application servers also provides a JSP tag library that lets you include Flex applications in JSPs. Add the following tag library declaration to your JSP page to import Flex 3 Tag Library in JSP: WebContent\_TODOList\jsp\html\TODOListPortletView.jsp. (See Listing 10.)


Listing 10. Include Flex tag library in JSP
<%@ taglib uri="FlexTagLib" prefix="mm" %>

You can either refer a separate MXML file or include MXML syntax directly in the JSP by the <mm:mxml> tag. (See Listing 11.)


Listing 11. Include Flex project in JSP
<mm:mxml source="TODOListFlex.mxml" width="100%" height="768">
</mm:mxml>

You can delete all old content of the JSP which is generated by RSA. You can refer to the JSP in the sample code.


Deployment

Make use of the J2EE Web application archive feature in RSA to export a WAR file so that you can install on a staging or production portal server.

Make sure that the constant USE_MOCK_DATA in the WebContent\_TODOList\jsp\html\TODOListFlex.mxml file has been set to false in order to use RemoteObject to get TODO list. Copy the DB2 JDBC driver jars from sample code to the WEB-INF\lib.

Do the following steps to export the WAR file from this sample project.

  1. Right-click this sample project name and select Export from the pop-up menu.
  2. Select WAR file in the Export window, and then select Next.
  3. Specify a location for the new WAR file.
  4. Click Finish and make sure you can find the WAR file under the location.

Log on to WebSphere Portal Server with the administrator role, and go to the Administrative page to install the WAR file by following these steps.

  1. If it's not already running, start the portal server. From the control panel select Portlet Management , Web Modules.
  2. On the "Manage Web Modules" page, click Install.
  3. On the "Installing a Web module" page, use the Browse button to locate the WAR file. Click Next and Finish. This step may take several minutes due to the large size. You can reduce the size of the WAR using shared lib. (Refer to the section, "Reduce WAR size".)

After installing the portlet, we need to place it into a page. From the Administrative page of the portal server do the following steps.

  1. Navigate to Portal --> User Interface --> Manage Pages.
  2. Locate the page to add a portlet and click Edit Page Layout.
  3. Add the portlet (TODOList) in the page, and click Done to save the changes.

Now go the page and verify the TODOList portlet.


Figure 9. TODOList Portlet
A screen shot shows the TODOList screen from before.  The title is TODOList.  Beneath the title is a label reading "Total 1 TODO item(s).  Then follows a table with 4 columns, labeled from left to right: Date, Subject and Owner.  The 4th column has no label.  One entry is shown in the table, with the Date as "2008-10-06," the Subject as "Submit the paper," and the Owner as "Sean."  The 4th column contains a box which is checked.  Below the table are data entry fields containing the same information listed in the table with an Add button.

If you host your portal server on a Linux® system (skip this section if you are using a Windows® system), the flash may not be displayed. You may need to configure the java.awt.headless parameter for JVM of portal server as value of true. Follow these steps to do so:

  1. Start the WebSphere Application Server: /opt/IBM/WebSphere/AppServer/bin/startServer.sh server1
  2. Open the WebSphere Application Server Administrative Console: http://server:10001/ibm/console
  3. Navigate to Server --> Application Server --> WebSphere_Portal --> Process Definition --> Java Virtual --> Custom Properties

Figure 10. Configure the java.awt.headless
A screen shot shows the administrative tool for Websphere on the screen Application servers, WebSphere Portal, Process Definition, Java Virtual Machine, Custom Properties.  The value java.awt.headless has been set to "true."
  1. Restart the WebSphere Application Server and Portal Server.

Reduce WAR size (shared lib)

This step is optional. Because the size of the WAR may be larger than 20MB, you can move some of the contents into the WebSphere Application Service shared lib in order to reduce the size. Refer to the section, "Create a shared library", in the developerWorks article, "Using resource environment providers in WebSphere Application Server". (See Resources.)


Summary

This article covered the major features and enhancements during the integration development between Adobe Flex and WebSphere Portal. We also examined the runtime environment used to show the sample application and how easy it can be to create an RIA application and integrate Portal features before delivering the applications for an improved user experience.



Download

Description Name Size Download method
Sample code TODOList.war 23MB HTTP

Information about download methods


Resources

Learn

Get products and technologies

Posted by 1010
반응형

 

swfobject_2_2.zip

 

What is SWFObject?

SWFObject 2:

  • Offers two optimized Flash Player embed methods; a markup based approach and a method that relies on JavaScript
  • Offers a JavaScript API that aims to provide a complete tool set for embedding SWF files and retrieving Flash Player related information
  • Utilizes only one small JavaScript file (10Kb / GZIPed: 3.9Kb)
  • Is the successor of SWFObject 1.5, UFO and the Adobe Flash Player Detection Kit
  • Intends to unify all existing Flash Player embed methods and provide a new standard for embedding Adobe Flash Player content

Why should you use SWFObject?

SWFObject 2:

  • Is more optimized and flexible than any other Flash Player embed method around
  • Offers one solution for everybody: It shouldn't matter if you are an HTML, Flash, or JavaScript developer, there should be something in it for everyone
  • Breaks the cycle of being locked into vendor specific markup and promotes the use of web standards and alternative content
  • Uses unobtrusive JavaScript and JavaScript best practices
  • Is easy to use

The A List Apart article Flash Embedding Cage Match describes the full rationale behind SWFObject 2.

Why does SWFObject use JavaScript?

SWFObject 2 primarily uses JavaScript to overcome issues that cannot be solved by markup alone; it:

  • Detects the Flash Player version and determines whether Flash content or alternative content should be shown, to avoid that outdated Flash plug-ins break Flash content
  • Offers functionality to revert to alternative content in case of an outdated plug-in by means of a DOM manipulation (Note: if no Flash plug-in is installed the HTML object element automatically falls back to its nested alternative content)
  • Offers the option to use Adobe Express Install to download the latest Flash Player
  • Offers a JavaScript API to perform common Flash Player and Flash content related tasks

Should I use the static or dynamic publishing method?

SWFObject 2 offers two distinct methods to embed Flash Player content:

  1. The static publishing method embeds both Flash content and alternative content using standards compliant markup, and uses JavaScript to resolve the issues that markup alone cannot solve
  2. The dynamic publishing method is based on marked up alternative content and uses JavaScript to replace this content with Flash content if the minimal Flash Player version is installed and enough JavaScript support is available (similar like previous versions of SWFObject and UFO)

The advantages of the static publishing method are:

  1. The actual authoring of standards compliant markup is promoted
  2. Best embed performance
  3. The mechanism of embedding Flash content does not rely on a scripting language, so your Flash content can reach a significant bigger audience:
    • If you have the Flash plug-in installed, but have JavaScript disabled or a use a browser that doesn't support JavaScript, you will still be able to see your Flash content
    • Flash will now also run on a device like Sony PSP, which has very poor JavaScript support
    • Automated tools like RSS readers are able to pick up Flash content

The advantages of the dynamic publishing method are:

  1. It integrates very well with scripted applications and enables the use of dynamic variables (flashvars)
  2. It avoids click-to-activate mechanisms to activate active content in Internet Explorer 6/7 and Opera 9+. Please note that Microsoft has phased out most active content from its Internet Explorer browsers

How to embed Flash Player content using SWFObject static publishing

STEP 1: Embed both Flash content and alternative content using standards compliant markup

SWFObject's base markup uses the nested-objects method (with proprietary Internet Explorer conditional comments. See Flash Embedding Cage Match) to ensure the most optimal cross-browser support by means of markup only, while being standards compliant and supporting alternative content (See Flash Embed Test Suite):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>SWFObject - step 1</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<div>

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="780" height="420">
<param name="movie" value="myContent.swf" />
<!--[if !IE]>-->
<object type="application/x-shockwave-flash" data="myContent.swf" width="780" height="420">
<!--<![endif]-->
<p>Alternative content</p>
<!--[if !IE]>-->
</object>
<!--<![endif]-->
</object>

</div>
</body>
</html>

NOTE: The nested-objects method requires a double object definition (the outer object targeting Internet Explorer and the inner object targeting all other browsers), so you need to define your object attributes and nested param elements twice.

Required attributes:

  • classid (outer object element only, value is always clsid:D27CDB6E-AE6D-11cf-96B8-444553540000)
  • type (inner object element only, value is always application/x-shockwave-flash)
  • data (inner object element only, defines the URL of a SWF)
  • width (both object elements, defines the width of a SWF)
  • height (both object elements, defines the height of a SWF)

Required param element:

  • movie (outer object element only, defines the URL of a SWF)

NOTE: We advise not to use the codebase attribute to point to the URL of the Flash plugin installer on Adobe's servers, because this is illegal according to the specifications which restrict its access to the domain of the current document only. We recommend the use of alternative content with a subtle message that a user can have a richer experience by downloading the Flash plugin instead.

How can you use HTML to configure your Flash content?

You can add the following often-used optional attributes to the object element:

  • id
  • name
  • class
  • align

You can use the following optional Flash specific param elements (more info):

Why should you use alternative content?

The object element allows you to nest alternative content inside of it, which will be displayed if Flash is not installed or supported. This content will also be picked up by search engines, making it a great tool for creating search-engine-friendly content. Summarizing, you should use alternative content when you like to create content that is accessible for people who browse the Web without plugins, create search-engine-friendly content or tell visitors that they can have a richer user experience by downloading the Flash plug-in.

STEP 2: Include the SWFObject JavaScript library in the head of your HTML page

The SWFObject library consists of one external JavaScript file. SWFObject will be executed as soon as it is read and will perform all DOM manipulations as soon as the DOM is loaded - for all browsers that support this, like IE, Firefox, Safari and Opera 9+ - or otherwise as soon as the onload event fires:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>SWFObject - step 2</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

<script type="text/javascript" src="swfobject.js"></script>

</head>
<body>
<div>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="780" height="420">
<param name="movie" value="myContent.swf" />
<!--[if !IE]>-->
<object type="application/x-shockwave-flash" data="myContent.swf" width="780" height="420">
<!--<![endif]-->
<p>Alternative content</p>
<!--[if !IE]>-->
</object>
<!--<![endif]-->
</object>
</div>
</body>
</html>

STEP 3: Register your Flash content with the SWFObject library and tell SWFObject what to do with it

First add a unique id to the outer object tag that defines your Flash content. Second add the swfobject.registerObject method:

  1. The first argument (String, required) specifies the id used in the markup.
  2. The second argument (String, required) specifies the Flash player version your content is published for. It activates the Flash version detection for a SWF to determine whether to show Flash content or force alternative content by doing a DOM manipulation. While Flash version numbers normally consist of major.minor.release.build, SWFObject only looks at the first 3 numbers, so both "WIN 9,0,18,0" (IE) or "Shockwave Flash 9 r18" (all other browsers) will translate to "9.0.18". If you only want to test for a major version you can omit the minor and release numbers, like "9" instead of "9.0.0".
  3. The third argument (String, optional) can be used to activate Adobe express install and specifies the URL of your express install SWF file. Express install displays a standardized Flash plugin download dialog instead of your Flash content when the required plugin version is not available. A default expressInstall.swf file is packaged with the project. It also contains the corresponding expressInstall.fla and AS files (in the SRC directory) to let you create your own custom express install experience. Please note that express install will only fire once (the first time that it is invoked), that it is only supported by Flash Player 6.0.65 or higher on Win or Mac platforms, and that it requires a minimal SWF size of 310x137px.
  4. The fourth argument (JavaScript function, optional) can be used to define a callback function that is called on both success or failure of creating a Flash plug-in <object> on the page (see API documentation)
  5. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
    <head>
    <title>SWFObject - step 3</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <script type="text/javascript" src="swfobject.js"></script>

    <script type="text/javascript">
    swfobject
    .registerObject("myId", "9.0.115", "expressInstall.swf");
    </script>

    </head>
    <body>
    <div>

    <object id="myId" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="780" height="420">

    <param name="movie" value="myContent.swf" />
    <!--[if !IE]>-->
    <object type="application/x-shockwave-flash" data="myContent.swf" width="780" height="420">
    <!--<![endif]-->
    <p>Alternative content</p>
    <!--[if !IE]>-->
    </object>
    <!--<![endif]-->
    </object>
    </div>
    </body>
    </html>

TIPS

  • Use the SWFObject HTML and JavaScript generator to help you author your code
  • Just repeat steps 1 and 3 to embed multiple SWF files into one HTML page
  • The easiest way to reference the active object element is by using the JavaScript API: `swfobject.getObjectById(objectIdStr)

How to embed Flash Player content using SWFObject dynamic publishing

STEP 1: Create alternative content using standards compliant markup

SWFObject's dynamic embed method follows the principle of progressive enhancement and replaces alternative HTML content for Flash content when enough JavaScript and Flash plug-in support is available. First define your alternative content and label it with an id:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>SWFObject dynamic embed - step 1</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>

<div id="myContent">
<p>Alternative content</p>
</div>

</body>
</html>

STEP 2: Include the SWFObject JavaScript library in the head of your HTML page

The SWFObject library consists of one external JavaScript file. SWFObject will be executed as soon as it is read and will perform all DOM manipulations as soon as the DOM is loaded - for all browsers that support this, like IE, Firefox, Safari and Opera 9+ - or otherwise as soon as the onload event fires:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>SWFObject dynamic embed - step 2</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />

<script type="text/javascript" src="swfobject.js"></script>

</head>
<body>
<div id="myContent">
<p>Alternative content</p>
</div>
</body>
</html>

STEP 3: Embed your SWF with JavaScript

swfobject.embedSWF(swfUrl, id, width, height, version, expressInstallSwfurl, flashvars, params, attributes, callbackFn) has five required and five optional arguments:

  1. swfUrl (String, required) specifies the URL of your SWF
  2. id (String, required) specifies the id of the HTML element (containing your alternative content) you would like to have replaced by your Flash content
  3. width (String, required) specifies the width of your SWF
  4. height (String, required) specifies the height of your SWF
  5. version (String, required) specifies the Flash player version your SWF is published for (format is: "major.minor.release" or "major")
  6. expressInstallSwfurl (String, optional) specifies the URL of your express install SWF and activates Adobe express install. Please note that express install will only fire once (the first time that it is invoked), that it is only supported by Flash Player 6.0.65 or higher on Win or Mac platforms, and that it requires a minimal SWF size of 310x137px.
  7. flashvars (Object, optional) specifies your flashvars with name:value pairs
  8. params (Object, optional) specifies your nested object element params with name:value pairs
  9. attributes (Object, optional) specifies your object's attributes with name:value pairs
  10. callbackFn (JavaScript function, optional) can be used to define a callback function that is called on both success or failure of creating a Flash plug-in <object> on the page (see API documentation)

NOTE: You can omit the optional parameters, as long as you don't break the parameter order. If you don't want to use an optional parameter, but would like to use a following optional parameter, you can simply pass false as its value. For the flashvars, params and attributes JavaScript Objects, you can also pass an empty object instead: {}.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>SWFObject dynamic embed - step 3</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script type="text/javascript" src="swfobject.js"></script>

<script type="text/javascript">
swfobject
.embedSWF("myContent.swf", "myContent", "300", "120", "9.0.0");
</script>

</head>
<body>
<div id="myContent">
<p>Alternative content</p>
</div>
</body>
</html>

How can you configure your Flash content?

You can add the following often-used optional attributes to the object element:

  • id (NOTE: when undefined, the object element automatically inherits the id from the alternative content container element)
  • align
  • name
  • styleclass (see note about class)
  • class

Note: class is a reserved ECMA4 keyword and will throw errors in Internet Explorer unless it is surrounded by quotation marks (e.g. "class" or 'class'). For this reason, swfobject provides the styleclass keyword as a safe alternative syntax for class; if you use styleclass, swfobject will automatically insert it as class for you.

Example:

var attributes = {
id
: "myId",
align
: "left",
styleclass
: "myclass"
};

If you would rather use class instead of styleclass, wrap the word class in quotes like this:

var attributes = {
id
: "myId",
align
: "left",
"class": "myclass"
};

You can use the following optional Flash specific param elements (more info):

How do you use JavaScript Objects to define your flashvars, params and object's attributes?

You can best define JavaScript Objects using the Object literal notation, which looks like:

<script type="text/javascript">

var flashvars = {};
var params = {};
var attributes = {};

swfobject
.embedSWF("myContent.swf", "myContent", "300", "120", "9.0.0","expressInstall.swf", flashvars, params, attributes);

</script>

You can add your name:value pairs while you define an object (note: please make sure not to put a comma behind the last name:value pair inside an object)

<script type="text/javascript">

var flashvars = {
name1
: "hello",
name2
: "world",
name3
: "foobar"
};
var params = {
menu
: "false"
};
var attributes = {
id
: "myDynamicContent",
name
: "myDynamicContent"
};

swfobject
.embedSWF("myContent.swf", "myContent", "300", "120", "9.0.0","expressInstall.swf", flashvars, params, attributes);

</script>

Or add properties and values after its creation by using a dot notation:

<script type="text/javascript">

var flashvars = {};
flashvars
.name1 = "hello";
flashvars
.name2 = "world";
flashvars
.name3 = "foobar";

var params = {};
params
.menu = "false";

var attributes = {};
attributes
.id = "myDynamicContent";
attributes
.name = "myDynamicContent";

swfobject
.embedSWF("myContent.swf", "myContent", "300", "120", "9.0.0","expressInstall.swf", flashvars, params, attributes);

</script>

Which can also be written as (the less readable shorthand version for the die-hard scripter who love one-liners):

<script type="text/javascript">

swfobject
.embedSWF("myContent.swf", "myContent", "300", "120", "9.0.0","expressInstall.swf", {name1:"hello",name2:"world",name3:"foobar"}, {menu:"false"}, {id:"myDynamicContent",name:"myDynamicContent"});

</script>

If you don't want to use an optional argument you can define it as false or as an empty Object (NOTE: from SWFObject 2.1 onwards you can also use null or 0):

<script type="text/javascript">

var flashvars = false;
var params = {};
var attributes = {
id
: "myDynamicContent",
name
: "myDynamicContent"
};

swfobject
.embedSWF("myContent.swf", "myContent", "300", "120", "9.0.0","expressInstall.swf", flashvars, params, attributes);

</script>

The flashvars Object is a shorthand notation that is there for your ease of use. You could potentially ignore it and specify your flashvars via the params Object:

<script type="text/javascript">

var flashvars = false;
var params = {
menu
: "false",
flashvars
: "name1=hello&name2=world&name3=foobar"
};
var attributes = {
id
: "myDynamicContent",
name
: "myDynamicContent"
};

swfobject
.embedSWF("myContent.swf", "myContent", "300", "120", "9.0.0","expressInstall.swf", flashvars, params, attributes);

</script>

TIPS

SWFObject 1.5 to SWFObject 2 migration tips

  1. SWFObject 2 is NOT backwards compatible with SWFObject 1.5
  2. It is now preferred to insert all script blocks in the head of your HTML page. Adding your scripts in the body of your page may have visual impact (e.g. flashes of replaced alternative content), because your JavaScript code will be executed in a later stage (the exact impact depends on your implementation)
  3. The library itself is now written in lowercase: swfobject instead of SWFObject
  4. Methods can only be accessed via the library (instead via a SWFObject instance in SWFObject 1.5)
  5. The JavaScript API is entirely different and more elaborate
  6. SWFObject 2 replaces your entire alternative HTML content block, including the referenced HTML container element, for Flash content when enough JavaScript and Flash support is available, while SWFObject 1.5 only replaces the content inside the referenced HTML container. When you don't specify an id attribute, the object element will automatically inherit the id of the HTML container element of your alternative content.

UFO to SWFObject 2 migration tips

  1. SWFObject 2 replaces your entire alternative HTML content block, including the referenced HTML container element, for Flash content when enough JavaScript and Flash support is available, while UFO only replaces the content inside the referenced HTML container. When you don't specify an id attribute, the object element will automatically inherit the id of the HTML container element of your alternative content.
  2. UFO's setcontainercss feature has not been incorporated in SWFObject 2, however it can easily be replicated by using the SWFObject JavaScript API, see: swfobject.createCSS(selStr, declStr)

Does SWFObject 2 support MIME type application/xhtml+xml?

SWFObject 2 does NOT support XML MIME types, which is a design decision.

There are a number of reasons why we are not supporting this:

  • Only a very small (non-significant) amount of web authors is using it
  • We are unsure if it is the direction to go. Internet Explorer does not support it and all other major browser vendors are aiming their arrows at a new standard way of HTML parsing (with HTML 5), which departs from the current W3C vision of parsing HTML as XML
  • We save a considerate amount of file size and effort (testing, issue resolving) by not supporting it

Tutorials (beginner level)

Comments

The comments functionality on this wiki has been switched off to avoid spam and abuse.

If you have any questions/comments about SWFObject or its documentation, or have problems with an implementation:

  1. Please make sure you have read our FAQ
  2. Use the SWFObject discussion group

If you find any bugs or want to request a future enhancement, you can fill in an issue report on the SWFObject issues page

Posted by 1010
반응형

Using the Console

The JavaScript Console provides two primary functions for developers testing web pages and applications:

  • A place to log diagnostic information using methods provided by the Console API, such as console.log(), or console.profile().
  • A shell prompt where you can enter commands and interact with the document and the Chrome DevTools. You can evaluate expressions directly in the Console, and can also use the methods provided by the Command Line API, such as $() command for selecting elements, or profile() to start the CPU profiler.

This documentation provides an overview and common uses of these two APIs. You can also browse the Console API and Command Line API reference guides.

Basic operation

Opening the Console

The JavaScript Console is available in two modes within Chrome DevTools: the primary Console tab, or as a split-view you can display while on another tab (such as Elements or Sources).

To open the Console tab, do one of the following:

  • Use the keyboard shortcut Command - Option - J (Mac) or Control -Shift -J (Windows/Linux).
  • Select View > Developer > JavaScript Console.

Console panel view

To toggle a split-view of the Console on another tab, press the Esc key on your keyboard, or click the Show/Hide Console button in the bottom left corner of the Chrome DevTools window. In the following screenshot the Console split-view is shown with the Elements panel.

Console split-view

Clearing the console history

To clear the console's history, do one of the following:

  • Right-click or Ctrl-click anywhere in the Console and choose Clear Console from the context menu that appears.
  • Enter the clear() Command Line API at the shell prompt.
  • Invoke console.clear() Console API from JavaScript.
  • Use the keyboard shortcut ⌘K or ⌃L (Mac) Control - L (Windows and Linux).

By default, the console history is cleared when you navigate to another page. You can change this behavior by enabling Preserve log upon navigation in the Console area of the Settings dialog (see Console preferences).

Console settings

The Console has two global settings you can modify in the General tab of the DevTools Settings dialog:

  • Log XMLHTTPRequests—determines if each XMLHTTPRequest is logged to the Console panel.
  • Preserve log upon navigation—determines if console history for the current page is preserved when you navigate to another page. By default, both of these settings are disabled.

You can also change these settings by right-clicking anywhere in the Console to bring up the context menu.

Console panel view

Using the Console API

The Console API is collection of methods provided by the global console object defined by DevTools. One of the API's main purposes is to log information (such as a property value, or an entire objects or DOM element) to the console while your application is running. You can also group output visually in the console to reduce visual clutter.

Writing to the console

The console.log() method takes one or more expressions as parameters and writes their current values to the console. For example:

var a = document.createElement('p');
a
.appendChild(document.createTextNode('foo'));
a
.appendChild(document.createTextNode('bar'));
console
.log("Node count: " + a.childNodes.length);

Console log output

Instead of concatenating expressions together with the "+" operator (as shown above), you can put each in its own method parameter and they will be joined together in a space-delimited line.

console.log("Node count:", a.childNodes.length, "and the current time is:", Date.now());

Console log output

Errors and warnings

The console.error() method displays a red icon along with the message text, which is colored red.

function connectToServer() {
console
.error("Error: %s (%i)", "Server is not responding",500);
}
connectToServer
();

The console.warn() method displays a yellow warning icon with the message text.

if(a.childNodes.length < 3 ) {
console
.warn('Warning! Too few nodes (%d)', a.childNodes.length);
}

Example of console.warn()

Assertions

The console.assert() method conditionally displays an error string (its second parameter) only if its first parameter evaluates to false. For instance, in the following example an error message is written to the console only if the number of child nodes belonging to the list element is greater than 500.

console.assert(list.childNodes.length < 500, "Node count is > 500");

Example of console.assert()

Filtering console output

You can quickly filter console output by its severity level--errors, warning, or standard log statements--by selecting one of the filter options along the bottom of the Console, as shown below.

Only show console.error() output

Filter options:

  • All—Shows all console output.
  • Errors—Only show output from console.error()
  • Warnings—Only show output from console.warn()
  • Logs—Only show output from console.log(), console.info() and console.debug().
  • Debug—Only show output from console.timeEnd() and other console output.

Grouping output

You can visually group related console output statements together in the console with the console.group() and groupEnd() commands.

var user = "jsmith", authenticated = false;
console
.group("Authentication phase");
console
.log("Authenticating user '%s'", user);
// authentication code here...
if (!authenticated) {
console
.log("User '%s' not authenticated.", user)
}
console
.groupEnd();

Logging group example

You can also nest logging groups. In the following example a logging group is created for the authentication phase of a login process. If the user is authenticated, a nested group is created for the authorization phase.

var user = "jsmith", authenticated = true, authorized = true;
// Top-level group
console
.group("Authenticating user '%s'", user);
if (authenticated) {
console
.log("User '%s' was authenticated", user);
// Start nested group
console
.group("Authorizing user '%s'", user);
if (authorized) {
console
.log("User '%s' was authorized.", user);
}
// End nested group
console
.groupEnd();
}
// End top-level group
console
.groupEnd();
console
.log("A group-less log trace.");

Nested logging group example

To create a group that is initially collapsed, use console.groupCollapsed() instead of console.group(), as shown below:

console.groupCollapsed("Authenticating user '%s'", user);
if (authenticated) {
...
}

Initially collapsed group

String substitution and formatting

The first parameter you pass to any of the console's logging methods (log() or error(), for example) may contain one or more format specifiers. A format specifier consists of a % symbol followed by a letter that indicates the formatting that should be applied to the inserted value (%s for strings, for example). The format specifier identifies where to substitute a value provided by a subsequent parameter value.

The following example using the %s (string) and %d (integer) formatters to insert values into the output string.

console.log("%s has %d points", "Sam", "100");

This would result in "Sam has 100 points" being logged to the console.

The following table lists the supported format specifiers and the formatting they apply:

Format specifier Description
%s Formats the value as a string.
%d or %i Formats the value as an integer.
%f Formats the object as a floating point value.
%o Formats the value as an expandable DOM element (as in the Elements panel).
%O Formats the value as an expandable JavaScript object.
%c Applies CSS style rules to output string specified by the second parameter.

In the following example the %d format specifier is substituted with the value of document.childNodes.length and formatted as an integer; the %f format specifier is substituted with the value returned by Date.now(), which is formatted as a floating point number.

console.log("Node count: %d, and the time is %f.", document.childNodes.length, Date.now());

Using format specifiers

Formatting DOM elements as JavaScript objects

By default, when you log a DOM element to the console it's displayed in an XML format, as in the Elements panel:

console.log(document.body.firstElementChild)

You can also log an element's JavaScript representation with the console.dir() method:

console.dir(document.body.firstElementChild);

Equivalently, you can us the %O format specifier with console.log():

console.log("%O", document.body.firstElementChild);

Styling console output with CSS

You use the %c format specifier to apply custom CSS rules to any string you write to the Console with console.log() or related methods.

console.log("%cThis will be formatted with large, blue text", "color: blue; font-size:18pt");

Styling console output with CSS

Measuring how long something takes

You can use the console.time() and console.timeEnd() methods to measure how long a function or operation in your code takes to complete. You call console.time() at the point in your code where you want to start the timer and console.timeEnd() to stop the timer. The elapsed time between these two calls is displayed in the console.

console.time("Array initialize");
var array= new Array(1000000);
for (var i = array.length - 1; i >= 0; i--) {
array
[i] = new Object();
};
console
.timeEnd("Array initialize");

Example of using console.time() and timeEnd()

Marking the Timeline

The Timeline panel gives you a complete overview of where time is spent when loading and using your web app or page. The console.timeStamp() method marks the Timeline at the moment it was executed. This provides an easy way to correlate events in your application with other browser-related events, such as layout or paints.

In the following example the Timeline is marked when the application enters the AddResult() function's implementation.

function AddResult(name, result) {
console
.timeStamp("Adding result");
var text = name + ': ' + result;
var results = document.getElementById("results");
results
.innerHTML += (text + "<br>");
}

As shown in the following screenshot, the timeStamp() command annotates the Timeline in the following places:

  • A yellow vertical line in the Timeline's summary and detail views.
  • A record is added to the list of recorded events.

Timeline showing custom timestamp

Setting breakpoints in JavaScript

You can start a debugging session from your JavaScript code by calling the debugger command. For instance, in the following example the JavaScript debugger is opened when an object's brightness() function is invoked:

brightness : function() {
debugger;
var r = Math.floor(this.red*255);
var g = Math.floor(this.green*255);
var b = Math.floor(this.blue*255);
return (r * 77 + g * 150 + b * 29) >> 8;
}

Example of using debugger command

Using the Command Line API

In addition to being a place where you can log information from your application, the Console is also a shell prompt where you can directly evaluate expressions or issue commands provided by the Command Line API. This API provides the following features:

  • Convenience functions for selecting DOM elements
  • Methods for controlling the CPU profiler
  • Aliases for a number of Console API methods
  • Monitoring events
  • View event listeners registered on objects

Evaluating expressions

The Console attempts to evaluate any JavaScript expression you enter at the shell prompt, upon pressing the Return or Enter key. The Console provides auto-completion and tab-completion. As you type expressions, property names are automatically suggested. If there are multiple properties with the same prefix, pressing the Tab key cycles through them. Pressing the right arrow key accepts the current suggestion. The current suggestion is also accepted by pressing the Tab key if there is only one matched property.

To enter a multi-line expression at the shell prompt (such as a function definition) press Shift+Enter between lines.

Selecting elements

The Command Line API provides several methods to access DOM elements in your application. For example, the $() method returns the first element that matches the specified CSS selector, just like document.querySelector(). For instance, the following code returns the element with the ID "loginBtn".

$('#loginBtn');

The $$() command returns an array of all the elements that match the specified CSS selector, just like document.querySelectorAll(). For instance, the following displays selects all <button> elements with the CSS class "loginBtn":

$$('button.loginBtn');

Lastly, the x() method takes an XPath path as a parameter and returns an array of all elements that match the specified path. The following returns all the <script> elements that are children of the <body> tag:

$x('/html/body/script');

Inspecting DOM elements and JavaScript heap objects

The inspect() method takes a DOM element reference (or JavaScript reference) as a parameter and displays the element or object in the appropriate panel—the Elements panel for DOM elements, or the Profile panel for a JavaScript object.

For example, in the following screenshot the $() function is used to get a reference to an <li> element. Then the last evaluated expression property ($_) is passed to inspect() to open that element in the Elements panel.

Accessing recently selected elements and objects

Often when testing you'll select DOM elements—either directly in the Elements panel or using the Selection tool (magnifying glass)—so that you can further inspect the element. Or, when analyzing a memory snapshot in the Profiles panel, you might select a JavaScript object to further inspect it.

The Console remembers the last five elements (or heap objects) you've selected and makes them available as properties named $0, $1, $2, $3 and $4. The most recently selected element or object is available as $0, the second most as $1, and so forth.

The following screenshot shows the values of these properties after selecting three different elements in turn from the Elements panel:

Recently selected elements

Monitoring events

The monitorEvents() command monitors an object for one or more specified events. When an event occurs on the monitored object, the corresponding Event object is logged to the Console. You specify the object and the events you want to monitor on that object. For example, the following code enables event monitoring for every "resize" event on the global window object.

monitorEvents(window, "resize");

Monitoring window resize events

To monitor several events, you can pass an array of event names as the second parameter. The code below monitors both "mousedown" and "mouseup" events on the body of the document.

monitorEvents(document.body, ["mousedown", "mouseup"]);

You can also pass one of the supported "event types" that DevTools maps to a set of actual event names. For example, the "touch" event type cause DevTools to monitor "touchstart", "touchend", "touchmove", and "touchcancel" events on the target object.

monitorEvents($('#scrollBar'), "touch");

See monitorEvents() in the Console API Reference for a list of supported event types.

To stop monitoring events call unmonitorEvents(), passing the object to stop monitoring.

unmonitorEvents(window);

Controlling the CPU profiler

You can create JavaScript CPU profiles from the command line with the profile() and profileEnd() commands. You can optionally specify a name that's applied to the profile you create.

For example, the following shows an example of creating a new profile with the default name:

The new profile appears in the Profiles panel under the name "Profile 1":

If you specify a label for the new profile, it is used as the new profile's heading. If you create multiple profiles with the same name, they are grouped as individual runs under the same heading:

The result in the Profiles panel:

CPU profiles can be nested, for example:

profile("A");
profile
("B");
profileEnd
("B")
profileEnd
("A")

The calls to stop and start profiling do not need be properly nested. For example, the following works the same as the previous example:

profile("A");
profile
("B");
profileEnd
("A");
profileEnd
("B");

 

Posted by 1010
반응형
 

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="absolute">
 <mx:Script>
       
  import mx.controls.Menu;
  import mx.events.MenuEvent;
  private var myMenu:Menu;
  private function createAndShow():void {
   ta1.text="";
   myMenu = Menu.createMenu(null, myMenuData, false);
   myMenu.labelField="@label";
   myMenu.addEventListener(MenuEvent.ITEM_ROLL_OVER,menuShowInfo);
   myMenu.show(225, 10);
  }
  private function menuShowInfo(event:MenuEvent):void {
   ta1.text="event.type: " + event.type;
   ta1.text+="\nevent.label: " + event.label;
   ta1.text+="\nevent.index: " + event.index;
   if (event.item) {
    ta1.text+="\nItem label: " + event.item.@label
    ta1.text+="\nItem selected: " + event.item.@toggled;
    ta1.text+= "\nItem type: " + event.item.@type;
   }
  }
  
  [Bindable]
  public var menuData:Array = [
   {label: "MenuItem A", children: [
    {label: "SubMenuItem A-1", enabled: false},
    {label: "SubMenuItem A-2", type: "normal"}]},
   {label: "MenuItem B", type: "check", toggled: true},
   {label: "MenuItem C", type: "check", toggled: false},
   {type: "separator"},
   {label: "MenuItem D", children: [
    {label: "SubMenuItem D-1", type: "radio", groupName: "g1"},
    {label: "SubMenuItem D-2", type: "radio", groupName: "g1",toggled: true},
    {label: "SubMenuItem D-3", type: "radio", groupName: "g1"}]}
  ];
  
 </mx:Script>
 <mx:XML id="myMenuData">
  <xmlRoot>
   <menuitem label="MenuItem A">
    <menuitem label="SubMenuItem A-1" enabled="false" />
    <menuitem label="SubMenuItem A-2" />
   </menuitem>
   <menuitem label="MenuItem B" type="check" toggled="true" />
   <menuitem label="MenuItem C" type="check" toggled="false" />
   <menuitem type="separator" />
   <menuitem label="MenuItem D">
    <menuitem label="SubMenuItem D-1" type="radio" groupName="one" />
    <menuitem label="SubMenuItem D-2" type="radio" groupName="one" toggled="true" />
    <menuitem label="SubMenuItem D-3" type="radio" groupName="one" />
   </menuitem>
  </xmlRoot>
 </mx:XML>
 <mx:Button x="10" y="5" label="Open XML Popup" click="createAndShow();" />
 <mx:TextArea x="10" y="70" width="200" height="300" id="ta1" />
</mx:Application>

 

Posted by 1010
00.Flex,Flash,ActionScript2013. 5. 28. 14:57
반응형

Setting the vertical gap between items in a Flex VBox container

By On January 3, 2008 · 7 Comments

The following example shows how you can control the amount of vertical spacing between items in a VBox container by setting the verticalGap style.

Full code after the jump.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2008/01/03/setting-the-vertical-gap-between-items-in-a-flex-vbox-container/ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
        layout="vertical"
        verticalAlign="middle"
        backgroundColor="white">
 
    <mx:Style>
        HSlider {
            dataTipPlacement: bottom;
            dataTipPrecision: 0;
        }
    </mx:Style>
 
    <mx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            import mx.events.SliderEvent;
 
            private function slider_change(evt:SliderEvent):void {
                vBox.setStyle("verticalGap", evt.value);
            }
 
            private function vBox_creationComplete(evt:FlexEvent):void {
                slider.value = vBox.getStyle("verticalGap");
            }
        ]]>
    </mx:Script>
 
    <mx:ApplicationControlBar dock="true">
        <mx:Form styleName="plain">
            <mx:FormItem label="verticalGap:"
                    direction="horizontal">
                <mx:HSlider id="slider"
                        minimum="0"
                        maximum="20"
                        liveDragging="true"
                        snapInterval="1"
                        tickInterval="1"
                        change="slider_change(event);" />
                <mx:Label text="{slider.value}" />
            </mx:FormItem>
        </mx:Form>
    </mx:ApplicationControlBar>
 
    <mx:VBox id="vBox"
            width="100%"
            creationComplete="vBox_creationComplete(event);">
        <mx:Box width="100%" height="50" backgroundColor="red" />
        <mx:Box width="100%" height="50" backgroundColor="haloOrange" />
        <mx:Box width="100%" height="50" backgroundColor="yellow" />
        <mx:Box width="100%" height="50" backgroundColor="haloGreen" />
        <mx:Box width="100%" height="50" backgroundColor="haloBlue" />
    </mx:VBox>
 
</mx:Application>

View source is enabled in the following example.

 

Posted by 1010