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