1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2005-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.printing
13{
14
15import flash.display.DisplayObject;
16import flash.display.Loader;
17import flash.display.Sprite;
18import flash.display.Stage;
19import flash.geom.Rectangle;
20import flash.printing.PrintJob;
21import flash.printing.PrintJobOptions;
22import mx.core.Application;
23import mx.core.IFlexDisplayObject;
24import mx.core.IUIComponent;
25import mx.core.UIComponent;
26import mx.core.UIComponentGlobals;
27import mx.core.mx_internal;
28
29use namespace mx_internal;
30
31/**
32 *  The FlexPrintJob class is a wrapper for the flash.printing.PrintJob class.
33 *  It supports automatically slicing and paginating the output on multilple pages,
34 *  and scaling the grid contents to fit the printer's page size.
35 *
36 *  @includeExample examples/FormPrintHeader.mxml -noswf
37 *  @includeExample examples/FormPrintFooter.mxml -noswf
38 *  @includeExample examples/FormPrintView.mxml -noswf
39 *  @includeExample examples/PrintDataGridExample.mxml
40 *
41 */
42public class FlexPrintJob
43{
44    include "../core/Version.as";
45
46    //--------------------------------------------------------------------------
47    //
48    //  Constructor
49    //
50    //--------------------------------------------------------------------------
51
52    /**
53     *  Constructor.
54     */
55    public function FlexPrintJob()
56    {
57        super();
58    }
59
60    //--------------------------------------------------------------------------
61    //
62    //  Properties
63    //
64    //--------------------------------------------------------------------------
65
66    /**
67     *  @private
68     */
69    private var printJob:PrintJob = new PrintJob();
70
71    //--------------------------------------------------------------------------
72    //
73    //  Properties
74    //
75    //--------------------------------------------------------------------------
76
77    //----------------------------------
78    //  pageHeight
79    //----------------------------------
80
81    /**
82     *  @private
83     *  Storage for the pageHeight property.
84     */
85    private var _pageHeight:Number = 0;
86
87    /**
88     *  The height  of the printable area on the printer page;
89     *  it does not include any user-set margins.
90     *  It is set after start() method returns.
91     */
92    public function get pageHeight():Number
93    {
94        return _pageHeight;
95    }
96
97    //----------------------------------
98    //  pageWidth
99    //----------------------------------
100
101    /**
102     *  @private
103     *  Storage for the pageWidth property.
104     */
105    private var _pageWidth:Number = 0;
106
107    /**
108     *  The width of the printable area on the printer page;
109     *  it does not include any user-set margins.
110     *  This property is set after <code>start()</code> method returns.
111     */
112    public function get pageWidth():Number
113    {
114        return _pageWidth;
115    }
116
117    //----------------------------------
118    //  printAsBitmap
119    //----------------------------------
120
121    /**
122     *  @private
123     *  Storage for the printAsBitmap property.
124     */
125    private var _printAsBitmap:Boolean = true;
126
127    /**
128     *  Specifies whether to print the job content as a bitmap (<code>true</code>)
129     *  or in vector format (<code>false</code>).
130     *  Printing as a bitmap supports output that includes a bitmap image with
131     *  alpha transparency or color effects.
132     *  If the content does not include any bitmap images with
133     *  alpha transparency or color effects, you can print in higher quality
134     *  vector format by setting the <code>printAsBitmap</code> property to
135     *  <code>false</code>.
136     *
137     *  @default true
138     */
139    public function get printAsBitmap():Boolean
140    {
141        return _printAsBitmap;
142    }
143
144    /**
145     *  @private
146     */
147    public function set printAsBitmap(value:Boolean):void
148    {
149        _printAsBitmap = value;
150    }
151
152    //--------------------------------------------------------------------------
153    //
154    //  Methods
155    //
156    //--------------------------------------------------------------------------
157
158    /**
159     *  Initializes the PrintJob object.
160     *  Displays the operating system printer dialog to the user.
161     *  Flex sets the <code>pageWidth</code> and <code>pageHeight</code>
162     *  properties after this call returns.
163     *
164     *  @return <code>true</code> if the user clicks OK
165     *  when the print dialog box appears, or <code>false</code> if the user
166     *  clicks Cancel or if an error occurs.
167     */
168    public function start():Boolean
169    {
170        var ok:Boolean = printJob.start();
171
172        if (ok)
173        {
174            _pageWidth = printJob.pageWidth;
175            _pageHeight = printJob.pageHeight;
176        }
177
178        return ok;
179    }
180
181    /**
182     *  Adds a UIComponent object to the list of objects being printed.
183     *  Call this method after the <code>start()</code> method returns.
184     *  Each call to this method starts a new page, so you should format
185     *  your objects in page-sized chunks.
186     *  You can use the PrintDataGrid class to span a data grid across
187     *  multiple pages.
188     *
189     *  @see PrintDataGrid
190     *  @see FlexPrintJobScaleType
191     *
192     *  @param obj The Object to be printed.
193     *
194     *  @param scaleType The scaling technique to use to control how the
195     *  object fits on one or more printed pages.
196     *  Must be one of the constant values defined in the FlexPrintJobScaleType
197     *  class.
198     */
199    public function addObject(obj:IUIComponent,
200                              scaleType:String = "matchWidth"):void
201    {
202        var objWidth:Number;
203        var objHeight:Number;
204
205        var objPercWidth:Number;
206        var objPercHeight:Number;
207
208        var n:int;
209        var i:int;
210        var j:int;
211
212        var child:IFlexDisplayObject;
213        var childPercentSizes:Object = {};
214
215        var appExplicitWidth:Number;
216        var appExplicitHeight:Number;
217
218        if (obj is Application)
219        {
220            // The following loop is required only for scenario where
221            // application may have a few children with percent
222            // width or height.
223            n = Application(obj).numChildren
224            for (i = 0; i < n; i++)
225            {
226                child = IFlexDisplayObject(Application(obj).getChildAt(i));
227
228                if (child is UIComponent &&
229                    (!isNaN(UIComponent(child).percentWidth) ||
230                     !isNaN(UIComponent(child).percentHeight)))
231                {
232                    childPercentSizes[child.name] = {};
233
234                    if (!isNaN(UIComponent(child).percentWidth) &&
235                        isNaN(UIComponent(child).explicitWidth))
236                    {
237                        childPercentSizes[child.name].percentWidth =
238                            UIComponent(child).percentWidth;
239                        UIComponent(child).percentWidth = NaN;
240                        UIComponent(child).explicitWidth =
241                            UIComponent(child).width;
242                    }
243
244                    if (!isNaN(UIComponent(child).percentHeight) &&
245                        isNaN(UIComponent(child).explicitHeight))
246                    {
247                        childPercentSizes[child.name].percentHeight =
248                            UIComponent(child).percentHeight;
249                        UIComponent(child).percentHeight = NaN;
250                        UIComponent(child).explicitHeight =
251                            UIComponent(child).height;
252                    }
253                }
254            }
255
256            if (!isNaN(UIComponent(obj).explicitWidth)
257                && !isNaN(UIComponent(obj).explicitHeight))
258            {
259                appExplicitWidth = UIComponent(obj).explicitWidth;
260                appExplicitHeight = UIComponent(obj).explicitHeight;
261
262                UIComponent(obj).explicitWidth = NaN;
263                UIComponent(obj).explicitHeight = NaN;
264
265                UIComponent(obj).measuredWidth = appExplicitWidth;
266                UIComponent(obj).measuredHeight = appExplicitHeight;
267            }
268
269            if (isNaN(obj.percentWidth) && isNaN(obj.percentHeight))
270                UIComponent(obj).invalidateSizeFlag = false;
271
272            UIComponent(obj).validateSize();
273
274            objWidth = obj.measuredWidth;
275            objHeight = obj.measuredHeight;
276        }
277        else
278        {
279            // Lock if the content is percent width or height.
280            if (!isNaN(obj.percentWidth) && isNaN(obj.explicitWidth))
281            {
282                objPercWidth = obj.percentWidth;
283                obj.percentWidth = NaN;
284                obj.explicitWidth = obj.width;
285            }
286
287            if (!isNaN(obj.percentHeight) && isNaN(obj.explicitHeight))
288            {
289                objPercHeight = obj.percentHeight;
290                obj.percentHeight = NaN;
291                obj.explicitHeight = obj.height;
292            }
293
294            objWidth = obj.getExplicitOrMeasuredWidth();
295            objHeight = obj.getExplicitOrMeasuredHeight();
296        }
297
298        var widthRatio:Number = _pageWidth/objWidth;
299        var heightRatio:Number = _pageHeight/objHeight;
300
301        var ratio:Number = 1;
302
303        if (scaleType == FlexPrintJobScaleType.SHOW_ALL)
304        {
305            // Smaller of the two ratios for showAll.
306            ratio = (widthRatio < heightRatio) ? widthRatio : heightRatio;
307        }
308        else if (scaleType == FlexPrintJobScaleType.FILL_PAGE)
309        {
310            // Bigger of the two ratios for fillPage.
311            ratio = (widthRatio > heightRatio) ? widthRatio : heightRatio;
312        }
313        else if (scaleType == FlexPrintJobScaleType.NONE)
314        {
315        }
316        else if (scaleType == FlexPrintJobScaleType.MATCH_HEIGHT)
317        {
318            ratio = heightRatio;
319        }
320        else
321        {
322            ratio = widthRatio;
323        }
324
325        // Scale it to the required value.
326        obj.scaleX *= ratio;
327        obj.scaleY *= ratio;
328
329        UIComponentGlobals.layoutManager.usePhasedInstantiation = false;
330        UIComponentGlobals.layoutManager.validateNow();
331
332        var arrPrintData:Array = prepareToPrintObject(obj);
333
334        if (obj is Application)
335        {
336            objWidth *= ratio;
337            objHeight *= ratio;
338        }
339        else
340        {
341            objWidth = obj.getExplicitOrMeasuredWidth();
342            objHeight = obj.getExplicitOrMeasuredHeight();
343        }
344
345        // Find the number of pages required in vertical and horizontal.
346        var hPages:int = Math.ceil(objWidth / _pageWidth);
347        var vPages:int = Math.ceil(objHeight / _pageHeight);
348
349        // when sent to addPage, scaling is to be ignored.
350        var incrX:Number = _pageWidth / ratio;
351        var incrY:Number = _pageHeight / ratio;
352
353        var lastPageWidth:Number = (objWidth % _pageWidth) / ratio;
354        var lastPageHeight:Number = (objHeight % _pageHeight) / ratio;
355
356        for (j = 0; j < vPages; j++)
357        {
358            for (i = 0; i < hPages; i++)
359            {
360                var r:Rectangle =
361                    new Rectangle(i * incrX, j * incrY, incrX, incrY);
362
363                // For last pages send only the remaining amount
364                // so that rest of the paper is printed white
365                // else it prints that in gray.
366                if (i == hPages - 1 && lastPageWidth != 0)
367                    r.width = lastPageWidth;
368
369                if (j == vPages - 1 && lastPageHeight != 0)
370                    r.height = lastPageHeight;
371
372                // The final edge may have got fractioned as
373                // contents may not be complete multiple of pageWidth/Height.
374                // This may result in a blank area at the end of page.
375                // Tthis rounding off ensures no small blank area in the end
376                // but results in some part of next page getting reprinted
377                // this page but it does not result in loss of any information.
378                r.width = Math.ceil(r.width);
379                r.height = Math.ceil(r.height);
380
381                var printJobOptions:PrintJobOptions = new PrintJobOptions();
382                printJobOptions.printAsBitmap = _printAsBitmap;
383
384                printJob.addPage(Sprite(obj), r, printJobOptions);
385            }
386        }
387
388        finishPrintObject(obj, arrPrintData);
389
390        // Scale it back.
391        obj.scaleX /= ratio;
392        obj.scaleY /= ratio;
393
394        if (obj is Application)
395        {
396            if (!isNaN(appExplicitWidth)) //&& !isNaN(appExplicitHeight))
397            {
398                UIComponent(obj).setActualSize(appExplicitWidth,appExplicitHeight);
399                //UIComponent(obj).explicitWidth = appExplicitWidth;
400                //UIComponent(obj).explicitHeight = appExplicitHeight;
401
402                appExplicitWidth = NaN;
403                appExplicitHeight = NaN;
404
405                UIComponent(obj).measuredWidth = 0;
406                UIComponent(obj).measuredHeight = 0;
407            }
408
409            // The following loop is required only for scenario
410            // where application may have a few children
411            // with percent width or height.
412            n = Application(obj).numChildren
413            for (i = 0; i < n; i++)
414            {
415                child = IFlexDisplayObject(Application(obj).getChildAt(i));
416                if (child is UIComponent && childPercentSizes[child.name])
417                {
418                    var childPercentSize:Object = childPercentSizes[child.name];
419                    if (childPercentSize &&
420                        !isNaN(childPercentSize.percentWidth))
421                    {
422                        UIComponent(child).percentWidth =
423                            childPercentSize.percentWidth;
424                        UIComponent(child).explicitWidth = NaN;
425                    }
426
427                    if (childPercentSize &&
428                        !isNaN(childPercentSize.percentHeight))
429                    {
430                        UIComponent(child).percentHeight =
431                            childPercentSize.percentHeight;
432                        UIComponent(child).explicitHeight = NaN;
433                    }
434                }
435            }
436            UIComponent(obj).invalidateSizeFlag = false;
437            UIComponent(obj).validateSize();
438        }
439        else
440        {
441            // Unlock if the content was percent width or height.
442            if (!isNaN(objPercWidth))
443            {
444                obj.percentWidth = objPercWidth;
445                obj.explicitWidth = NaN;
446            }
447
448            if (!isNaN(objPercHeight))
449            {
450                obj.percentHeight = objPercHeight;
451                obj.explicitHeight = NaN;
452            }
453        }
454
455        UIComponentGlobals.layoutManager.usePhasedInstantiation = false;
456        UIComponentGlobals.layoutManager.validateNow();
457    }
458
459    /**
460     *  Sends the added objects to the printer to start printing.
461     *  Call this method after you have used the <code>addObject()</code>
462     *  method to add the print pages.
463     */
464    public function send():void
465    {
466        printJob.send();
467    }
468
469    /**
470     *  @private
471     *  Prepare the target and its parents to print.
472     *  If the content is inside a Container with scrollBars,
473     *  it still gets printed all right.
474     */
475    private function prepareToPrintObject(target:IUIComponent):Array
476    {
477        var arrPrintData:Array = [];
478
479        var obj:DisplayObject
480            = (target is DisplayObject) ? DisplayObject(target) : null;
481        var index:Number = 0;
482
483        while (obj)
484        {
485            if (obj is UIComponent)
486                arrPrintData[index++]
487                    = UIComponent(obj).prepareToPrint(UIComponent(target));
488            else if (obj is DisplayObject && !(obj is Stage))
489            {
490                arrPrintData[index++] = DisplayObject(obj).mask;
491                DisplayObject(obj).mask = null;
492            }
493
494            obj = (obj.parent is DisplayObject) ?
495                  DisplayObject(obj.parent) :
496                  null;
497        }
498
499        return arrPrintData;
500    }
501
502    /**
503     *  @private
504     *  Reverts the target and its parents back from Print state,
505     */
506    private function finishPrintObject(target:IUIComponent,
507                                       arrPrintData:Array):void
508    {
509        var obj:DisplayObject
510            = (target is DisplayObject) ? DisplayObject(target) : null;
511        var index:Number = 0;
512        while (obj)
513        {
514            if (obj is UIComponent)
515                UIComponent(obj).finishPrint(arrPrintData[index++],
516                                                UIComponent(target));
517            else if (obj is DisplayObject && !(obj is Stage))
518            {
519                DisplayObject(obj).mask = arrPrintData[index++];
520            }
521
522            obj = (obj.parent is DisplayObject) ?
523                  DisplayObject(obj.parent) :
524                  null;
525        }
526    }
527}
528
529}
530