PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/Sample/mx/printing/FlexPrintJob.as

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