PreparedPages.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.IO;
  5. using System.Drawing;
  6. using System.ComponentModel;
  7. using FastReport.Utils;
  8. namespace FastReport.Preview
  9. {
  10. /// <summary>
  11. /// Specifies an action that will be performed on <b>PreparedPages.AddPage</b> method call.
  12. /// </summary>
  13. public enum AddPageAction
  14. {
  15. /// <summary>
  16. /// Do not add the new prepared page if possible, increment the <b>CurPage</b> instead.
  17. /// </summary>
  18. WriteOver,
  19. /// <summary>
  20. /// Add the new prepared page.
  21. /// </summary>
  22. Add
  23. }
  24. /// <summary>
  25. /// Represents the pages of a prepared report.
  26. /// </summary>
  27. /// <remarks>
  28. /// <para>Prepared page is a page that you can see in the preview window. Prepared pages can be
  29. /// accessed via <see cref="FastReport.Report.PreparedPages"/> property.</para>
  30. /// <para>The common scenarios of using this object are:
  31. /// <list type="bullet">
  32. /// <item>
  33. /// <description>Working with prepared pages after the report is finished: load
  34. /// (<see cref="Load(string)"/>) or save (<see cref="Save(string)"/>) pages
  35. /// from/to a .fpx file, get a page with specified index to work with its objects
  36. /// (<see cref="GetPage"/>); modify specified page (<see cref="ModifyPage"/>).
  37. /// </description>
  38. /// </item>
  39. /// <item>
  40. /// <description>Using the <see cref="AddPage"/>, <see cref="AddSourcePage"/>, <see cref="AddBand"/>
  41. /// methods while report is generating to produce an output.
  42. /// </description>
  43. /// </item>
  44. /// </list>
  45. /// </para>
  46. /// </remarks>
  47. [ToolboxItem(false)]
  48. public partial class PreparedPages : IDisposable
  49. {
  50. #region Fields
  51. private SourcePages sourcePages;
  52. private List<PreparedPage> preparedPages;
  53. private Dictionary dictionary;
  54. private Bookmarks bookmarks;
  55. private Outline outline;
  56. private BlobStore blobStore;
  57. private int curPage;
  58. private AddPageAction addPageAction;
  59. private Report report;
  60. private PageCache pageCache;
  61. private FileStream tempFile;
  62. private bool canUpload;
  63. private string tempFileName;
  64. private XmlItem cutObjects;
  65. private Dictionary<string, object> macroValues;
  66. private int firstPassPage;
  67. private int firstPassPosition;
  68. #endregion
  69. #region Properties
  70. internal Report Report
  71. {
  72. get { return report; }
  73. }
  74. internal SourcePages SourcePages
  75. {
  76. get { return sourcePages; }
  77. }
  78. internal Dictionary Dictionary
  79. {
  80. get { return dictionary; }
  81. }
  82. internal Bookmarks Bookmarks
  83. {
  84. get { return bookmarks; }
  85. }
  86. internal Outline Outline
  87. {
  88. get { return outline; }
  89. }
  90. internal BlobStore BlobStore
  91. {
  92. get { return blobStore; }
  93. }
  94. internal FileStream TempFile
  95. {
  96. get { return tempFile; }
  97. }
  98. internal Dictionary<string, object> MacroValues
  99. {
  100. get { return macroValues; }
  101. }
  102. internal int CurPosition
  103. {
  104. get
  105. {
  106. if (CurPage < 0 || CurPage >= Count)
  107. return 0;
  108. return preparedPages[CurPage].CurPosition;
  109. }
  110. }
  111. internal int CurPage
  112. {
  113. get { return curPage; }
  114. set { curPage = value; }
  115. }
  116. /// <summary>
  117. /// Gets the number of pages in the prepared report.
  118. /// </summary>
  119. public int Count
  120. {
  121. get { return preparedPages.Count; }
  122. }
  123. /// <summary>
  124. /// Gets the XML for rendering the outline of the report
  125. /// </summary>
  126. public XmlItem OutlineXml
  127. {
  128. get => outline.Xml;
  129. }
  130. /// <summary>
  131. /// Specifies an action that will be performed on <see cref="AddPage"/> method call.
  132. /// </summary>
  133. public AddPageAction AddPageAction
  134. {
  135. get { return addPageAction; }
  136. set { addPageAction = value; }
  137. }
  138. /// <summary>
  139. /// Gets or sets a value indicating whether the prepared pages can be uploaded to the file cache.
  140. /// </summary>
  141. /// <remarks>
  142. /// <para>This property is used while report is generating.</para>
  143. /// <para>Default value for this property is <b>true</b>. That means the prepared pages may be uploaded to
  144. /// the file cache if needed. To prevent this (for example, if you need to access some objects
  145. /// on previously generated pages), set the property value to <b>false</b>.</para>
  146. /// </remarks>
  147. public bool CanUploadToCache
  148. {
  149. get { return canUpload; }
  150. set
  151. {
  152. if (canUpload != value)
  153. {
  154. canUpload = value;
  155. if (value)
  156. UploadPages();
  157. }
  158. }
  159. }
  160. #endregion
  161. #region Private Methods
  162. private void UploadPages()
  163. {
  164. if (Report.UseFileCache)
  165. {
  166. for (int i = 0; i < Count - 1; i++)
  167. {
  168. preparedPages[i].Upload();
  169. }
  170. }
  171. }
  172. #endregion
  173. #region Protected Methods
  174. /// <inheritdoc/>
  175. public void Dispose()
  176. {
  177. Clear();
  178. if (tempFile != null)
  179. {
  180. tempFile.Dispose();
  181. tempFile = null;
  182. if (File.Exists(tempFileName))
  183. File.Delete(tempFileName);
  184. }
  185. BlobStore.Dispose();
  186. }
  187. #endregion
  188. #region Public Methods
  189. internal void SetReport(Report report)
  190. {
  191. this.report = report;
  192. }
  193. /// <summary>
  194. /// Adds a source page to the prepared pages dictionary.
  195. /// </summary>
  196. /// <param name="page">The template page to add.</param>
  197. /// <remarks>
  198. /// Call this method before using <b>AddPage</b> and <b>AddBand</b> methods. This method adds
  199. /// a page to the dictionary that will be used to decrease size of the prepared report.
  200. /// </remarks>
  201. public void AddSourcePage(ReportPage page)
  202. {
  203. SourcePages.Add(page);
  204. }
  205. /// <summary>
  206. /// Adds a new page.
  207. /// </summary>
  208. /// <param name="page">The original (template) page to add.</param>
  209. /// <remarks>
  210. /// Call the <see cref="AddSourcePage"/> method before adding a page. This method creates
  211. /// a new output page with settings based on <b>page</b> parameter.
  212. /// </remarks>
  213. public void AddPage(ReportPage page)
  214. {
  215. CurPage++;
  216. if (CurPage >= Count || AddPageAction != AddPageAction.WriteOver)
  217. {
  218. PreparedPage preparedPage = new PreparedPage(page, this);
  219. preparedPages.Add(preparedPage);
  220. // upload previous page to the file cache if enabled
  221. if (CanUploadToCache && Count > 1)
  222. preparedPages[Count - 2].Upload();
  223. AddPageAction = AddPageAction.WriteOver;
  224. CurPage = Count - 1;
  225. Report.Engine.IncLogicalPageNumber();
  226. }
  227. }
  228. /// <summary>
  229. /// Prints a band with all its child objects.
  230. /// </summary>
  231. /// <param name="band">The band to print.</param>
  232. /// <returns><b>true</b> if band was printed; <b>false</b> if it can't be printed
  233. /// on current page due to its <b>PrintOn</b> property value.</returns>
  234. /// <remarks>
  235. /// Call the <see cref="AddPage"/> method before adding a band.
  236. /// </remarks>
  237. public bool AddBand(BandBase band)
  238. {
  239. return preparedPages[CurPage].AddBand(band);
  240. }
  241. /// <summary>
  242. /// Gets a page with specified index.
  243. /// </summary>
  244. /// <param name="index">Zero-based index of page.</param>
  245. /// <returns>The page with specified index.</returns>
  246. public ReportPage GetPage(int index)
  247. {
  248. if (index >= 0 && index < preparedPages.Count)
  249. {
  250. macroValues["Page#"] = index + Report.InitialPageNumber;
  251. macroValues["TotalPages#"] = preparedPages.Count + Report.InitialPageNumber - 1;
  252. ReportPage page = preparedPages[index].GetPage();
  253. if (page.MirrorMargins && (index + 1) % 2 == 0)
  254. {
  255. float f = page.LeftMargin;
  256. page.LeftMargin = page.RightMargin;
  257. page.RightMargin = f;
  258. }
  259. return page;
  260. }
  261. else
  262. return null;
  263. }
  264. internal PreparedPage GetPreparedPage(int index)
  265. {
  266. if (index >= 0 && index < preparedPages.Count)
  267. {
  268. macroValues["Page#"] = index + Report.InitialPageNumber;
  269. macroValues["TotalPages#"] = preparedPages.Count + Report.InitialPageNumber - 1;
  270. return preparedPages[index];
  271. }
  272. else
  273. return null;
  274. }
  275. internal ReportPage GetCachedPage(int index)
  276. {
  277. return pageCache.Get(index);
  278. }
  279. /// <summary>
  280. /// Gets the size of specified page, in pixels.
  281. /// </summary>
  282. /// <param name="index">Index of page.</param>
  283. /// <returns>the size of specified page, in pixels.</returns>
  284. public SizeF GetPageSize(int index)
  285. {
  286. return preparedPages[index].PageSize;
  287. }
  288. /// <summary>
  289. /// Replaces the prepared page with specified one.
  290. /// </summary>
  291. /// <param name="index">The index of prepared page to replace.</param>
  292. /// <param name="newPage">The new page to replace with.</param>
  293. public void ModifyPage(int index, ReportPage newPage)
  294. {
  295. PreparedPage preparedPage = new PreparedPage(newPage, this);
  296. foreach (Base obj in newPage.ChildObjects)
  297. {
  298. if (obj is BandBase)
  299. preparedPage.AddBand(obj as BandBase);
  300. }
  301. preparedPages[index].Dispose();
  302. preparedPages[index] = preparedPage;
  303. pageCache.Remove(index);
  304. }
  305. /// <summary>
  306. /// Modify the prepared page with new sizes.
  307. /// </summary>
  308. /// <param name="name">The name of prepared page to reSize.</param>
  309. public void ModifyPageSize(string name)
  310. {
  311. foreach (PreparedPage page in preparedPages)
  312. {
  313. if (String.Equals(name, page.GetName(), StringComparison.InvariantCultureIgnoreCase))
  314. {
  315. page.ReCalcSizes();
  316. }
  317. }
  318. }
  319. /// <summary>
  320. /// Removes a page with the specified index.
  321. /// </summary>
  322. /// <param name="index">The zero-based index of page to remove.</param>
  323. public void RemovePage(int index)
  324. {
  325. preparedPages[index].Dispose();
  326. preparedPages.RemoveAt(index);
  327. pageCache.Clear();
  328. }
  329. /// <summary>
  330. /// Creates a copy of a page with specified index and inserts it after original one.
  331. /// </summary>
  332. /// <param name="index">The zero-based index of original page.</param>
  333. public void CopyPage(int index)
  334. {
  335. // insert a new empty page at specified index
  336. PreparedPage newPage = new PreparedPage(null, this);
  337. if (index == preparedPages.Count - 1)
  338. preparedPages.Add(newPage);
  339. else
  340. preparedPages.Insert(index + 1, newPage);
  341. // and copy source page into it
  342. ModifyPage(index + 1, GetPage(index));
  343. pageCache.Clear();
  344. }
  345. internal void InterleaveWithBackPage(int backPageIndex)
  346. {
  347. PreparedPage page = preparedPages[backPageIndex];
  348. int count = backPageIndex - 1;
  349. for (int i = 0; i < count; i++)
  350. {
  351. preparedPages.Insert(i * 2 + 1, page);
  352. }
  353. }
  354. internal void ApplyWatermark(Watermark watermark)
  355. {
  356. SourcePages.ApplyWatermark(watermark);
  357. pageCache.Clear();
  358. }
  359. internal void CutObjects(int index)
  360. {
  361. cutObjects = preparedPages[CurPage].CutObjects(index);
  362. }
  363. internal void PasteObjects(float x, float y)
  364. {
  365. preparedPages[CurPage].PasteObjects(cutObjects, x, y);
  366. }
  367. internal float GetLastY()
  368. {
  369. return preparedPages[CurPage].GetLastY();
  370. }
  371. internal void PrepareToFirstPass()
  372. {
  373. firstPassPage = CurPage;
  374. firstPassPosition = CurPosition;
  375. Outline.PrepareToFirstPass();
  376. }
  377. internal void ClearFirstPass()
  378. {
  379. Bookmarks.ClearFirstPass();
  380. Outline.ClearFirstPass();
  381. // clear all pages after specified FFirstPassPage
  382. while (firstPassPage < Count - 1)
  383. {
  384. RemovePage(Count - 1);
  385. }
  386. // if position is at begin, clear all pages
  387. if (firstPassPage == 0 && firstPassPosition == 0)
  388. RemovePage(0);
  389. // delete objects on the FFirstPassPage
  390. if (firstPassPage >= 0 && firstPassPage < Count)
  391. preparedPages[firstPassPage].CutObjects(firstPassPosition).Dispose();
  392. CurPage = firstPassPage;
  393. }
  394. internal bool ContainsBand(Type bandType)
  395. {
  396. return preparedPages[CurPage].ContainsBand(bandType);
  397. }
  398. internal bool ContainsBand(string bandName)
  399. {
  400. return preparedPages[CurPage].ContainsBand(bandName);
  401. }
  402. /// <summary>
  403. /// Saves prepared pages to a stream.
  404. /// </summary>
  405. /// <param name="stream">The stream to save to.</param>
  406. public void Save(Stream stream)
  407. {
  408. if (Config.PreparedCompressed)
  409. stream = Compressor.Compress(stream);
  410. using (XmlDocument doc = new XmlDocument())
  411. {
  412. doc.AutoIndent = true;
  413. doc.Root.Name = "preparedreport";
  414. // save ReportInfo
  415. doc.Root.SetProp("ReportInfo.Name", Report.ReportInfo.Name);
  416. doc.Root.SetProp("ReportInfo.Author", Report.ReportInfo.Author);
  417. doc.Root.SetProp("ReportInfo.Description", Report.ReportInfo.Description);
  418. doc.Root.SetProp("ReportInfo.Created", SystemFake.DateTime.Now.ToString());
  419. doc.Root.SetProp("ReportInfo.Modified", SystemFake.DateTime.Now.ToString());
  420. doc.Root.SetProp("ReportInfo.CreatorVersion", Report.ReportInfo.CreatorVersion);
  421. XmlItem pages = doc.Root.Add();
  422. pages.Name = "pages";
  423. // attach each page's xml to the doc
  424. foreach (PreparedPage page in preparedPages)
  425. {
  426. page.Load();
  427. pages.AddItem(page.Xml);
  428. }
  429. XmlItem sourcePages = doc.Root.Add();
  430. sourcePages.Name = "sourcepages";
  431. SourcePages.Save(sourcePages);
  432. XmlItem dictionary = doc.Root.Add();
  433. dictionary.Name = "dictionary";
  434. Dictionary.Save(dictionary);
  435. XmlItem bookmarks = doc.Root.Add();
  436. bookmarks.Name = "bookmarks";
  437. Bookmarks.Save(bookmarks);
  438. doc.Root.AddItem(Outline.Xml);
  439. XmlItem blobStore = doc.Root.Add();
  440. blobStore.Name = "blobstore";
  441. BlobStore.Save(blobStore);
  442. doc.Save(stream);
  443. // detach each page's xml from the doc
  444. foreach (PreparedPage page in preparedPages)
  445. {
  446. page.Xml.Parent = null;
  447. page.ClearUploadedXml();
  448. }
  449. Outline.Xml.Parent = null;
  450. }
  451. if (Config.PreparedCompressed)
  452. stream.Dispose();
  453. }
  454. /// <summary>
  455. /// Saves prepared pages to a .fpx file.
  456. /// </summary>
  457. /// <param name="fileName">The name of the file to save to.</param>
  458. public void Save(string fileName)
  459. {
  460. using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
  461. {
  462. Save(stream);
  463. }
  464. }
  465. /// <summary>
  466. /// Loads prepared pages from a stream.
  467. /// </summary>
  468. /// <param name="stream">The stream to load from.</param>
  469. public void Load(Stream stream)
  470. {
  471. Clear();
  472. if (stream.Length == 0)
  473. return;
  474. if (!stream.CanSeek)
  475. {
  476. MemoryStream tempStream = new MemoryStream();
  477. const int BUFFER_SIZE = 32768;
  478. stream.CopyTo(tempStream, BUFFER_SIZE);
  479. tempStream.Position = 0;
  480. stream = tempStream;
  481. }
  482. bool compressed = Compressor.IsStreamCompressed(stream);
  483. if (compressed)
  484. stream = Compressor.Decompress(stream, false);
  485. try
  486. {
  487. using (XmlDocument doc = new XmlDocument())
  488. {
  489. doc.Load(stream);
  490. XmlItem sourcePages = doc.Root.FindItem("sourcepages");
  491. SourcePages.Load(sourcePages);
  492. XmlItem dictionary = doc.Root.FindItem("dictionary");
  493. Dictionary.Load(dictionary);
  494. XmlItem bookmarks = doc.Root.FindItem("bookmarks");
  495. Bookmarks.Load(bookmarks);
  496. XmlItem outline = doc.Root.FindItem("outline");
  497. Outline.Xml = outline;
  498. XmlItem blobStore = doc.Root.FindItem("blobstore");
  499. BlobStore.LoadDestructive(blobStore);
  500. XmlItem pages = doc.Root.FindItem("pages");
  501. while (pages.Count > 0)
  502. {
  503. XmlItem pageItem = pages[0];
  504. PreparedPage preparedPage = new PreparedPage(null, this);
  505. preparedPages.Add(preparedPage);
  506. preparedPage.Xml = pageItem;
  507. }
  508. // load ReportInfo
  509. Report.ReportInfo.Name = doc.Root.GetProp("ReportInfo.Name");
  510. Report.ReportInfo.Author = doc.Root.GetProp("ReportInfo.Author");
  511. Report.ReportInfo.Description = doc.Root.GetProp("ReportInfo.Description");
  512. DateTime createDate;
  513. if (DateTime.TryParse(doc.Root.GetProp("ReportInfo.Created"), out createDate))
  514. Report.ReportInfo.Created = createDate;
  515. if (DateTime.TryParse(doc.Root.GetProp("ReportInfo.Modified"), out createDate))
  516. Report.ReportInfo.Modified = createDate;
  517. Report.ReportInfo.CreatorVersion = doc.Root.GetProp("ReportInfo.CreatorVersion");
  518. }
  519. }
  520. finally
  521. {
  522. if (compressed)
  523. stream.Dispose();
  524. }
  525. }
  526. /// <summary>
  527. /// Loads prepared pages from a .fpx file.
  528. /// </summary>
  529. /// <param name="fileName">The name of the file to load from.</param>
  530. public void Load(string fileName)
  531. {
  532. using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
  533. {
  534. Load(stream);
  535. }
  536. }
  537. /// <summary>
  538. /// Clears the prepared report's pages.
  539. /// </summary>
  540. public void Clear()
  541. {
  542. sourcePages.Clear();
  543. pageCache.Clear();
  544. foreach (PreparedPage page in preparedPages)
  545. {
  546. page.Dispose();
  547. }
  548. preparedPages.Clear();
  549. dictionary.Clear();
  550. bookmarks.Clear();
  551. outline.Clear();
  552. blobStore.Clear();
  553. curPage = 0;
  554. }
  555. internal void ClearPageCache()
  556. {
  557. pageCache.Clear();
  558. }
  559. #endregion
  560. /// <summary>
  561. /// Creates the pages of a prepared report
  562. /// </summary>
  563. /// <param name="report"></param>
  564. public PreparedPages(Report report)
  565. {
  566. this.report = report;
  567. sourcePages = new SourcePages(this);
  568. preparedPages = new List<PreparedPage>();
  569. dictionary = new Dictionary(this);
  570. bookmarks = new Bookmarks();
  571. outline = new Outline();
  572. blobStore = new BlobStore(report != null ? report.UseFileCache : false);
  573. pageCache = new PageCache(this);
  574. macroValues = new Dictionary<string, object>();
  575. if (this.report != null && this.report.UseFileCache)
  576. {
  577. tempFileName = Config.CreateTempFile("");
  578. tempFile = new FileStream(tempFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
  579. }
  580. }
  581. }
  582. }