blob: 53ab966d552252b08202d0f7bc3e5a372366ff89 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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
//
//--------------------------------------------------------------------------
//----------------------------------
// printJob
//----------------------------------
/**
* @private
* Storage for the printJob property.
*/
private var _printJob:PrintJob = new PrintJob();
/**
* The printJob property;
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 4.9
*/
public function get printJob():PrintJob
{
return _printJob;
}
//----------------------------------
// 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 <code>start()</code> 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 (<code>true</code>)
* or in vector format (<code>false</code>).
* 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 <code>printAsBitmap</code> property to
* <code>false</code>.
*
* @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 <code>pageWidth</code> and <code>pageHeight</code>
* properties after this call returns.
*
* @return <code>true</code> if the user clicks OK
* when the print dialog box appears, or <code>false</code> 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 <code>start()</code> 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 <code>addObject()</code>
* 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;
}
}
}
}