//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.printing { import flash.display.DisplayObject; import flash.display.Loader; import flash.display.Sprite; import flash.display.Stage; import flash.geom.Rectangle; import flash.printing.PrintJob; import flash.printing.PrintJobOptions; import mx.core.FlexVersion; import mx.core.IFlexDisplayObject; import mx.core.IUIComponent; import mx.core.UIComponent; import mx.core.UIComponentGlobals; import mx.core.mx_internal; use namespace mx_internal; /** * 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. * * @includeExample examples/FormPrintHeader.mxml -noswf * @includeExample examples/FormPrintFooter.mxml -noswf * @includeExample examples/FormPrintView.mxml -noswf * @includeExample examples/PrintDataGridExample.mxml * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class FlexPrintJob { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function FlexPrintJob() { super(); } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- /** * @private */ private var printJob:PrintJob = new PrintJob(); //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // pageHeight //---------------------------------- /** * @private * Storage for the pageHeight property. */ private var _pageHeight:Number = 0; /** * 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. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get pageHeight():Number { return _pageHeight; } //---------------------------------- // pageWidth //---------------------------------- /** * @private * Storage for the pageWidth property. */ private var _pageWidth:Number = 0; /** * 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. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get pageWidth():Number { return _pageWidth; } //---------------------------------- // printAsBitmap //---------------------------------- /** * @private * Storage for the printAsBitmap property. */ private var _printAsBitmap:Boolean = true; /** * 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. * * @default true * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get printAsBitmap():Boolean { return _printAsBitmap; } /** * @private */ public function set printAsBitmap(value:Boolean):void { _printAsBitmap = value; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * Initializes the PrintJob object. * Displays the operating system printer dialog to the user. * Flex sets the pageWidth and pageHeight * properties after this call returns. * * @return true if the user clicks OK * when the print dialog box appears, or false if the user * clicks Cancel or if an error occurs. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function start():Boolean { var ok:Boolean = printJob.start(); if (ok) { _pageWidth = printJob.pageWidth; _pageHeight = printJob.pageHeight; } return ok; } /** * 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. * * @see PrintDataGrid * @see FlexPrintJobScaleType * * @param obj The Object to be printed. * * @param scaleType 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. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function addObject(obj:IUIComponent, scaleType:String = "matchWidth"):void { var objWidth:Number; var objHeight:Number; var objPercWidth:Number; var objPercHeight:Number; var n:int; var i:int; var j:int; var child:IFlexDisplayObject; var childPercentSizes:Object = {}; var appExplicitWidth:Number; var appExplicitHeight:Number; var applicationClass:Class = Class(obj.systemManager.getDefinitionByName("mx.core::Application")); var fxApplicationClass:Class = Class(obj.systemManager.getDefinitionByName("spark.components::Application")); if ((applicationClass && obj is applicationClass) || (fxApplicationClass && obj is fxApplicationClass)) { // The following loop is required only for scenario where // application may have a few children with percent // width or height. n = obj["numElements"]; for (i = 0; i < n; i++) { child = IFlexDisplayObject(obj["getElementAt"](i)); if (child is UIComponent && (!isNaN(UIComponent(child).percentWidth) || !isNaN(UIComponent(child).percentHeight))) { childPercentSizes[child.name] = {}; if (!isNaN(UIComponent(child).percentWidth) && isNaN(UIComponent(child).explicitWidth)) { childPercentSizes[child.name].percentWidth = UIComponent(child).percentWidth; UIComponent(child).percentWidth = NaN; UIComponent(child).explicitWidth = UIComponent(child).width; } if (!isNaN(UIComponent(child).percentHeight) && isNaN(UIComponent(child).explicitHeight)) { childPercentSizes[child.name].percentHeight = UIComponent(child).percentHeight; UIComponent(child).percentHeight = NaN; UIComponent(child).explicitHeight = UIComponent(child).height; } } } if (!isNaN(UIComponent(obj).explicitWidth) && !isNaN(UIComponent(obj).explicitHeight)) { appExplicitWidth = UIComponent(obj).explicitWidth; appExplicitHeight = UIComponent(obj).explicitHeight; UIComponent(obj).explicitWidth = NaN; UIComponent(obj).explicitHeight = NaN; UIComponent(obj).measuredWidth = appExplicitWidth; UIComponent(obj).measuredHeight = appExplicitHeight; } if (isNaN(obj.percentWidth) && isNaN(obj.percentHeight)) UIComponent(obj).invalidateSizeFlag = false; UIComponent(obj).validateSize(); objWidth = obj.measuredWidth; objHeight = obj.measuredHeight; if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0) { objWidth *= obj.scaleX; objHeight *= obj.scaleY; } } else { // Lock if the content is percent width or height. if (!isNaN(obj.percentWidth) && isNaN(obj.explicitWidth)) { objPercWidth = obj.percentWidth; obj.percentWidth = NaN; obj.explicitWidth = obj.width; } if (!isNaN(obj.percentHeight) && isNaN(obj.explicitHeight)) { objPercHeight = obj.percentHeight; obj.percentHeight = NaN; obj.explicitHeight = obj.height; } objWidth = obj.getExplicitOrMeasuredWidth(); objHeight = obj.getExplicitOrMeasuredHeight(); if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0) { objWidth *= obj.scaleX; objHeight *= obj.scaleY; } } var widthRatio:Number = _pageWidth/objWidth; var heightRatio:Number = _pageHeight/objHeight; var ratio:Number = 1; if (scaleType == FlexPrintJobScaleType.SHOW_ALL) { // Smaller of the two ratios for showAll. ratio = (widthRatio < heightRatio) ? widthRatio : heightRatio; } else if (scaleType == FlexPrintJobScaleType.FILL_PAGE) { // Bigger of the two ratios for fillPage. ratio = (widthRatio > heightRatio) ? widthRatio : heightRatio; } else if (scaleType == FlexPrintJobScaleType.NONE) { } else if (scaleType == FlexPrintJobScaleType.MATCH_HEIGHT) { ratio = heightRatio; } else { ratio = widthRatio; } // Scale it to the required value. obj.scaleX *= ratio; obj.scaleY *= ratio; UIComponentGlobals.layoutManager.usePhasedInstantiation = false; UIComponentGlobals.layoutManager.validateNow(); var arrPrintData:Array = prepareToPrintObject(obj); if ((applicationClass && obj is applicationClass) || (fxApplicationClass && obj is fxApplicationClass)) { objWidth *= ratio; objHeight *= ratio; } else { objWidth = obj.getExplicitOrMeasuredWidth(); objHeight = obj.getExplicitOrMeasuredHeight(); if (FlexVersion.compatibilityVersion >= FlexVersion.VERSION_4_0) { objWidth *= obj.scaleX; objHeight *= obj.scaleY; } } // Find the number of pages required in vertical and horizontal. var hPages:int = Math.ceil(objWidth / _pageWidth); var vPages:int = Math.ceil(objHeight / _pageHeight); // when sent to addPage, scaling is to be ignored. var incrX:Number = _pageWidth / ratio; var incrY:Number = _pageHeight / ratio; var lastPageWidth:Number = (objWidth % _pageWidth) / ratio; var lastPageHeight:Number = (objHeight % _pageHeight) / ratio; for (j = 0; j < vPages; j++) { for (i = 0; i < hPages; i++) { var r:Rectangle = new Rectangle(i * incrX, j * incrY, incrX, incrY); // For last pages send only the remaining amount // so that rest of the paper is printed white // else it prints that in gray. if (i == hPages - 1 && lastPageWidth != 0) r.width = lastPageWidth; if (j == vPages - 1 && lastPageHeight != 0) r.height = lastPageHeight; // The final edge may have got fractioned as // contents may not be complete multiple of pageWidth/Height. // This may result in a blank area at the end of page. // Tthis rounding off ensures no small blank area in the end // but results in some part of next page getting reprinted // this page but it does not result in loss of any information. r.width = Math.ceil(r.width); r.height = Math.ceil(r.height); var printJobOptions:PrintJobOptions = new PrintJobOptions(); printJobOptions.printAsBitmap = _printAsBitmap; printJob.addPage(Sprite(obj), r, printJobOptions); } } finishPrintObject(obj, arrPrintData); // Scale it back. obj.scaleX /= ratio; obj.scaleY /= ratio; if ((applicationClass && obj is applicationClass) || (fxApplicationClass && obj is fxApplicationClass)) { if (!isNaN(appExplicitWidth)) //&& !isNaN(appExplicitHeight)) { // use setActualSize so it doesn't invalidate. // nobody else should be resizing unless it is a sub-app UIComponent(obj).setActualSize(appExplicitWidth,appExplicitHeight); // is it a sub-app? if (!obj.systemManager.isTopLevelRoot()) { // invalidate for sub-apps since they have to be re-layed out by // the SWFLoader in some cases UIComponent(obj).explicitWidth = appExplicitWidth; UIComponent(obj).explicitHeight = appExplicitHeight; } appExplicitWidth = NaN; appExplicitHeight = NaN; UIComponent(obj).measuredWidth = 0; UIComponent(obj).measuredHeight = 0; } // The following loop is required only for scenario // where application may have a few children // with percent width or height. n = obj["numElements"]; for (i = 0; i < n; i++) { child = IFlexDisplayObject(obj["getElementAt"](i)); if (child is UIComponent && childPercentSizes[child.name]) { var childPercentSize:Object = childPercentSizes[child.name]; if (childPercentSize && !isNaN(childPercentSize.percentWidth)) { UIComponent(child).percentWidth = childPercentSize.percentWidth; UIComponent(child).explicitWidth = NaN; } if (childPercentSize && !isNaN(childPercentSize.percentHeight)) { UIComponent(child).percentHeight = childPercentSize.percentHeight; UIComponent(child).explicitHeight = NaN; } } } UIComponent(obj).invalidateSizeFlag = false; UIComponent(obj).validateSize(); } else { // Unlock if the content was percent width or height. if (!isNaN(objPercWidth)) { obj.percentWidth = objPercWidth; obj.explicitWidth = NaN; } if (!isNaN(objPercHeight)) { obj.percentHeight = objPercHeight; obj.explicitHeight = NaN; } } UIComponentGlobals.layoutManager.usePhasedInstantiation = false; UIComponentGlobals.layoutManager.validateNow(); } /** * 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. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function send():void { printJob.send(); } /** * @private * Prepare the target and its parents to print. * If the content is inside a Container with scrollBars, * it still gets printed all right. */ private function prepareToPrintObject(target:IUIComponent):Array { var arrPrintData:Array = []; var obj:DisplayObject = (target is DisplayObject) ? DisplayObject(target) : null; var index:Number = 0; while (obj) { if (obj is UIComponent) arrPrintData[index++] = UIComponent(obj).prepareToPrint(UIComponent(target)); else if (obj is DisplayObject && !(obj is Stage)) { arrPrintData[index++] = DisplayObject(obj).mask; DisplayObject(obj).mask = null; } obj = (obj.parent is DisplayObject) ? DisplayObject(obj.parent) : null; } return arrPrintData; } /** * @private * Reverts the target and its parents back from Print state, */ private function finishPrintObject(target:IUIComponent, arrPrintData:Array):void { var obj:DisplayObject = (target is DisplayObject) ? DisplayObject(target) : null; var index:Number = 0; while (obj) { if (obj is UIComponent) UIComponent(obj).finishPrint(arrPrintData[index++], UIComponent(target)); else if (obj is DisplayObject && !(obj is Stage)) { DisplayObject(obj).mask = arrPrintData[index++]; } obj = (obj.parent is DisplayObject) ? DisplayObject(obj.parent) : null; } } } }