PreparedPages.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  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. internal event EventHandler PageAdded;
  161. #endregion
  162. #region Private Methods
  163. private void UploadPages()
  164. {
  165. if (Report.UseFileCache)
  166. {
  167. for (int i = 0; i < Count - 1; i++)
  168. {
  169. preparedPages[i].Upload();
  170. }
  171. }
  172. }
  173. #endregion
  174. #region Protected Methods
  175. /// <inheritdoc/>
  176. public void Dispose()
  177. {
  178. Clear();
  179. if (tempFile != null)
  180. {
  181. tempFile.Dispose();
  182. tempFile = null;
  183. if (File.Exists(tempFileName))
  184. File.Delete(tempFileName);
  185. }
  186. BlobStore.Dispose();
  187. }
  188. #endregion
  189. #region Public Methods
  190. internal void SetReport(Report report)
  191. {
  192. this.report = report;
  193. }
  194. /// <summary>
  195. /// Adds a source page to the prepared pages dictionary.
  196. /// </summary>
  197. /// <param name="page">The template page to add.</param>
  198. /// <remarks>
  199. /// Call this method before using <b>AddPage</b> and <b>AddBand</b> methods. This method adds
  200. /// a page to the dictionary that will be used to decrease size of the prepared report.
  201. /// </remarks>
  202. public void AddSourcePage(ReportPage page)
  203. {
  204. SourcePages.Add(page);
  205. }
  206. /// <summary>
  207. /// Adds a new page.
  208. /// </summary>
  209. /// <param name="page">The original (template) page to add.</param>
  210. /// <remarks>
  211. /// Call the <see cref="AddSourcePage"/> method before adding a page. This method creates
  212. /// a new output page with settings based on <b>page</b> parameter.
  213. /// </remarks>
  214. public void AddPage(ReportPage page)
  215. {
  216. if (page.Visible)
  217. {
  218. CurPage++;
  219. if (CurPage >= Count || AddPageAction != AddPageAction.WriteOver)
  220. {
  221. PreparedPage preparedPage = new PreparedPage(page, this);
  222. preparedPages.Add(preparedPage);
  223. // upload previous page to the file cache if enabled
  224. if (CanUploadToCache && Count > 1)
  225. preparedPages[Count - 2].Upload();
  226. AddPageAction = AddPageAction.WriteOver;
  227. CurPage = Count - 1;
  228. Report.Engine.IncLogicalPageNumber();
  229. PageAdded?.Invoke(this, EventArgs.Empty);
  230. }
  231. }
  232. }
  233. /// <summary>
  234. /// Prints a band with all its child objects.
  235. /// </summary>
  236. /// <param name="band">The band to print.</param>
  237. /// <returns><b>true</b> if band was printed; <b>false</b> if it can't be printed
  238. /// on current page due to its <b>PrintOn</b> property value.</returns>
  239. /// <remarks>
  240. /// Call the <see cref="AddPage"/> method before adding a band.
  241. /// </remarks>
  242. public bool AddBand(BandBase band)
  243. {
  244. return preparedPages[CurPage].AddBand(band);
  245. }
  246. /// <summary>
  247. /// Gets a page with specified index.
  248. /// </summary>
  249. /// <param name="index">Zero-based index of page.</param>
  250. /// <returns>The page with specified index.</returns>
  251. public ReportPage GetPage(int index)
  252. {
  253. if (index >= 0 && index < preparedPages.Count)
  254. {
  255. macroValues["Page#"] = index + Report.InitialPageNumber;
  256. macroValues["TotalPages#"] = preparedPages.Count + Report.InitialPageNumber - 1;
  257. ReportPage page = preparedPages[index].GetPage();
  258. if (page.MirrorMargins && (index + 1) % 2 == 0)
  259. {
  260. float f = page.LeftMargin;
  261. page.LeftMargin = page.RightMargin;
  262. page.RightMargin = f;
  263. }
  264. return page;
  265. }
  266. else
  267. return null;
  268. }
  269. internal PreparedPage GetPreparedPage(int index)
  270. {
  271. if (index >= 0 && index < preparedPages.Count)
  272. {
  273. macroValues["Page#"] = index + Report.InitialPageNumber;
  274. macroValues["TotalPages#"] = preparedPages.Count + Report.InitialPageNumber - 1;
  275. return preparedPages[index];
  276. }
  277. else
  278. return null;
  279. }
  280. internal ReportPage GetCachedPage(int index)
  281. {
  282. return pageCache.Get(index);
  283. }
  284. /// <summary>
  285. /// Gets the size of specified page, in pixels.
  286. /// </summary>
  287. /// <param name="index">Index of page.</param>
  288. /// <returns>the size of specified page, in pixels.</returns>
  289. public SizeF GetPageSize(int index)
  290. {
  291. return preparedPages[index].PageSize;
  292. }
  293. /// <summary>
  294. /// Replaces the prepared page with specified one.
  295. /// </summary>
  296. /// <param name="index">The index of prepared page to replace.</param>
  297. /// <param name="newPage">The new page to replace with.</param>
  298. public void ModifyPage(int index, ReportPage newPage)
  299. {
  300. PreparedPage preparedPage = new PreparedPage(newPage, this);
  301. foreach (Base obj in newPage.ChildObjects)
  302. {
  303. if (obj is BandBase)
  304. preparedPage.AddBand(obj as BandBase);
  305. }
  306. preparedPages[index].Dispose();
  307. preparedPages[index] = preparedPage;
  308. pageCache.Remove(index);
  309. }
  310. /// <summary>
  311. /// Modify the prepared page with new sizes.
  312. /// </summary>
  313. /// <param name="name">The name of prepared page to reSize.</param>
  314. public void ModifyPageSize(string name)
  315. {
  316. foreach (PreparedPage page in preparedPages)
  317. {
  318. if (String.Equals(name, page.GetName(), StringComparison.InvariantCultureIgnoreCase))
  319. {
  320. page.ReCalcSizes();
  321. }
  322. }
  323. }
  324. /// <summary>
  325. /// Removes a page with the specified index.
  326. /// </summary>
  327. /// <param name="index">The zero-based index of page to remove.</param>
  328. public void RemovePage(int index)
  329. {
  330. preparedPages[index].Dispose();
  331. preparedPages.RemoveAt(index);
  332. pageCache.Clear();
  333. }
  334. /// <summary>
  335. /// Creates a copy of a page with specified index and inserts it after original one.
  336. /// </summary>
  337. /// <param name="index">The zero-based index of original page.</param>
  338. public void CopyPage(int index)
  339. {
  340. // insert a new empty page at specified index
  341. PreparedPage newPage = new PreparedPage(null, this);
  342. if (index == preparedPages.Count - 1)
  343. preparedPages.Add(newPage);
  344. else
  345. preparedPages.Insert(index + 1, newPage);
  346. // and copy source page into it
  347. ModifyPage(index + 1, GetPage(index));
  348. pageCache.Clear();
  349. }
  350. internal void InterleaveWithBackPage(int backPageIndex)
  351. {
  352. PreparedPage page = preparedPages[backPageIndex];
  353. int count = backPageIndex - 1;
  354. for (int i = 0; i < count; i++)
  355. {
  356. preparedPages.Insert(i * 2 + 1, page);
  357. }
  358. }
  359. internal void ApplyWatermark(Watermark watermark)
  360. {
  361. SourcePages.ApplyWatermark(watermark);
  362. pageCache.Clear();
  363. }
  364. internal void CutObjects(int index)
  365. {
  366. cutObjects = preparedPages[CurPage].CutObjects(index);
  367. }
  368. internal void PasteObjects(float x, float y)
  369. {
  370. preparedPages[CurPage].PasteObjects(cutObjects, x, y);
  371. }
  372. internal float GetLastY()
  373. {
  374. return preparedPages[CurPage].GetLastY();
  375. }
  376. internal void PrepareToFirstPass()
  377. {
  378. firstPassPage = CurPage;
  379. firstPassPosition = CurPosition;
  380. Outline.PrepareToFirstPass();
  381. }
  382. internal void ClearFirstPass()
  383. {
  384. Bookmarks.ClearFirstPass();
  385. Outline.ClearFirstPass();
  386. // clear all pages after specified FFirstPassPage
  387. while (firstPassPage < Count - 1)
  388. {
  389. RemovePage(Count - 1);
  390. }
  391. // if position is at begin, clear all pages
  392. if (firstPassPage == 0 && firstPassPosition == 0)
  393. RemovePage(0);
  394. // delete objects on the FFirstPassPage
  395. if (firstPassPage >= 0 && firstPassPage < Count)
  396. preparedPages[firstPassPage].CutObjects(firstPassPosition).Dispose();
  397. CurPage = firstPassPage;
  398. }
  399. internal bool ContainsBand(Type bandType)
  400. {
  401. return preparedPages[CurPage].ContainsBand(bandType);
  402. }
  403. internal bool ContainsBand(string bandName)
  404. {
  405. return preparedPages[CurPage].ContainsBand(bandName);
  406. }
  407. /// <summary>
  408. /// Saves prepared pages to a stream.
  409. /// </summary>
  410. /// <param name="stream">The stream to save to.</param>
  411. public void Save(Stream stream)
  412. {
  413. if (Config.PreparedCompressed)
  414. stream = Compressor.Compress(stream);
  415. using (XmlDocument doc = new XmlDocument())
  416. {
  417. doc.AutoIndent = true;
  418. doc.Root.Name = "preparedreport";
  419. // save ReportInfo
  420. doc.Root.SetProp("ReportInfo.Name", Report.ReportInfo.Name);
  421. doc.Root.SetProp("ReportInfo.Author", Report.ReportInfo.Author);
  422. doc.Root.SetProp("ReportInfo.Description", Report.ReportInfo.Description);
  423. doc.Root.SetProp("ReportInfo.Created", SystemFake.DateTime.Now.ToString());
  424. doc.Root.SetProp("ReportInfo.Modified", SystemFake.DateTime.Now.ToString());
  425. doc.Root.SetProp("ReportInfo.CreatorVersion", Report.ReportInfo.CreatorVersion);
  426. XmlItem pages = doc.Root.Add();
  427. pages.Name = "pages";
  428. // attach each page's xml to the doc
  429. foreach (PreparedPage page in preparedPages)
  430. {
  431. page.Load();
  432. pages.AddItem(page.Xml);
  433. }
  434. XmlItem sourcePages = doc.Root.Add();
  435. sourcePages.Name = "sourcepages";
  436. SourcePages.Save(sourcePages);
  437. XmlItem dictionary = doc.Root.Add();
  438. dictionary.Name = "dictionary";
  439. Dictionary.Save(dictionary);
  440. XmlItem bookmarks = doc.Root.Add();
  441. bookmarks.Name = "bookmarks";
  442. Bookmarks.Save(bookmarks);
  443. doc.Root.AddItem(Outline.Xml);
  444. XmlItem blobStore = doc.Root.Add();
  445. blobStore.Name = "blobstore";
  446. BlobStore.Save(blobStore);
  447. doc.Save(stream);
  448. // detach each page's xml from the doc
  449. foreach (PreparedPage page in preparedPages)
  450. {
  451. page.Xml.Parent = null;
  452. page.ClearUploadedXml();
  453. }
  454. Outline.Xml.Parent = null;
  455. }
  456. if (Config.PreparedCompressed)
  457. stream.Dispose();
  458. }
  459. /// <summary>
  460. /// Saves prepared pages to a .fpx file.
  461. /// </summary>
  462. /// <param name="fileName">The name of the file to save to.</param>
  463. public void Save(string fileName)
  464. {
  465. using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
  466. {
  467. Save(stream);
  468. }
  469. }
  470. /// <summary>
  471. /// Loads prepared pages from a stream.
  472. /// </summary>
  473. /// <param name="stream">The stream to load from.</param>
  474. public void Load(Stream stream)
  475. {
  476. Clear();
  477. if (stream.Length == 0)
  478. return;
  479. if (!stream.CanSeek)
  480. {
  481. MemoryStream tempStream = new MemoryStream();
  482. const int BUFFER_SIZE = 32768;
  483. stream.CopyTo(tempStream, BUFFER_SIZE);
  484. tempStream.Position = 0;
  485. stream = tempStream;
  486. }
  487. bool compressed = Compressor.IsStreamCompressed(stream);
  488. if (compressed)
  489. stream = Compressor.Decompress(stream, false);
  490. try
  491. {
  492. using (XmlDocument doc = new XmlDocument())
  493. {
  494. doc.Load(stream);
  495. XmlItem sourcePages = doc.Root.FindItem("sourcepages");
  496. SourcePages.Load(sourcePages);
  497. XmlItem dictionary = doc.Root.FindItem("dictionary");
  498. Dictionary.Load(dictionary);
  499. XmlItem bookmarks = doc.Root.FindItem("bookmarks");
  500. Bookmarks.Load(bookmarks);
  501. XmlItem outline = doc.Root.FindItem("outline");
  502. Outline.Xml = outline;
  503. XmlItem blobStore = doc.Root.FindItem("blobstore");
  504. BlobStore.LoadDestructive(blobStore);
  505. XmlItem pages = doc.Root.FindItem("pages");
  506. while (pages.Count > 0)
  507. {
  508. XmlItem pageItem = pages[0];
  509. PreparedPage preparedPage = new PreparedPage(null, this);
  510. preparedPages.Add(preparedPage);
  511. preparedPage.Xml = pageItem;
  512. }
  513. // load ReportInfo
  514. Report.ReportInfo.Name = doc.Root.GetProp("ReportInfo.Name");
  515. Report.ReportInfo.Author = doc.Root.GetProp("ReportInfo.Author");
  516. Report.ReportInfo.Description = doc.Root.GetProp("ReportInfo.Description");
  517. DateTime createDate;
  518. if (DateTime.TryParse(doc.Root.GetProp("ReportInfo.Created"), out createDate))
  519. Report.ReportInfo.Created = createDate;
  520. if (DateTime.TryParse(doc.Root.GetProp("ReportInfo.Modified"), out createDate))
  521. Report.ReportInfo.Modified = createDate;
  522. Report.ReportInfo.CreatorVersion = doc.Root.GetProp("ReportInfo.CreatorVersion");
  523. }
  524. }
  525. finally
  526. {
  527. if (compressed)
  528. stream.Dispose();
  529. }
  530. }
  531. /// <summary>
  532. /// Loads prepared pages from a .fpx file.
  533. /// </summary>
  534. /// <param name="fileName">The name of the file to load from.</param>
  535. public void Load(string fileName)
  536. {
  537. using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
  538. {
  539. Load(stream);
  540. }
  541. }
  542. /// <summary>
  543. /// Clears the prepared report's pages.
  544. /// </summary>
  545. public void Clear()
  546. {
  547. sourcePages.Clear();
  548. pageCache.Clear();
  549. foreach (PreparedPage page in preparedPages)
  550. {
  551. page.Dispose();
  552. }
  553. preparedPages.Clear();
  554. dictionary.Clear();
  555. bookmarks.Clear();
  556. outline.Clear();
  557. blobStore.Clear();
  558. curPage = 0;
  559. }
  560. internal void ClearPageCache()
  561. {
  562. pageCache.Clear();
  563. }
  564. internal void RemovePageCache(int index)
  565. {
  566. pageCache.Remove(index);
  567. }
  568. #endregion
  569. /// <summary>
  570. /// Creates the pages of a prepared report
  571. /// </summary>
  572. /// <param name="report"></param>
  573. public PreparedPages(Report report)
  574. {
  575. this.report = report;
  576. sourcePages = new SourcePages(this);
  577. preparedPages = new List<PreparedPage>();
  578. dictionary = new Dictionary(this);
  579. bookmarks = new Bookmarks();
  580. outline = new Outline();
  581. blobStore = new BlobStore(report != null ? report.UseFileCache : false);
  582. pageCache = new PageCache(this);
  583. macroValues = new Dictionary<string, object>();
  584. if (this.report != null && this.report.UseFileCache)
  585. {
  586. tempFileName = Config.CreateTempFile("");
  587. tempFile = new FileStream(tempFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
  588. }
  589. }
  590. }
  591. }