using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Drawing; using System.ComponentModel; using FastReport.Utils; namespace FastReport.Preview { /// /// Specifies an action that will be performed on PreparedPages.AddPage method call. /// public enum AddPageAction { /// /// Do not add the new prepared page if possible, increment the CurPage instead. /// WriteOver, /// /// Add the new prepared page. /// Add } /// /// Represents the pages of a prepared report. /// /// /// Prepared page is a page that you can see in the preview window. Prepared pages can be /// accessed via property. /// The common scenarios of using this object are: /// /// /// Working with prepared pages after the report is finished: load /// () or save () pages /// from/to a .fpx file, get a page with specified index to work with its objects /// (); modify specified page (). /// /// /// /// Using the , , /// methods while report is generating to produce an output. /// /// /// /// /// [ToolboxItem(false)] public partial class PreparedPages : IDisposable { #region Fields private SourcePages sourcePages; private List preparedPages; private Dictionary dictionary; private Bookmarks bookmarks; private Outline outline; private BlobStore blobStore; private int curPage; private AddPageAction addPageAction; private Report report; private PageCache pageCache; private FileStream tempFile; private bool canUpload; private string tempFileName; private XmlItem cutObjects; private Dictionary macroValues; private int firstPassPage; private int firstPassPosition; #endregion #region Properties internal Report Report { get { return report; } } internal SourcePages SourcePages { get { return sourcePages; } } internal Dictionary Dictionary { get { return dictionary; } } internal Bookmarks Bookmarks { get { return bookmarks; } } internal Outline Outline { get { return outline; } } internal BlobStore BlobStore { get { return blobStore; } } internal FileStream TempFile { get { return tempFile; } } internal Dictionary MacroValues { get { return macroValues; } } internal int CurPosition { get { if (CurPage < 0 || CurPage >= Count) return 0; return preparedPages[CurPage].CurPosition; } } internal int CurPage { get { return curPage; } set { curPage = value; } } /// /// Gets the number of pages in the prepared report. /// public int Count { get { return preparedPages.Count; } } /// /// Gets the XML for rendering the outline of the report /// public XmlItem OutlineXml { get => outline.Xml; } /// /// Specifies an action that will be performed on method call. /// public AddPageAction AddPageAction { get { return addPageAction; } set { addPageAction = value; } } /// /// Gets or sets a value indicating whether the prepared pages can be uploaded to the file cache. /// /// /// This property is used while report is generating. /// Default value for this property is true. That means the prepared pages may be uploaded to /// the file cache if needed. To prevent this (for example, if you need to access some objects /// on previously generated pages), set the property value to false. /// public bool CanUploadToCache { get { return canUpload; } set { if (canUpload != value) { canUpload = value; if (value) UploadPages(); } } } #endregion #region Private Methods private void UploadPages() { if (Report.UseFileCache) { for (int i = 0; i < Count - 1; i++) { preparedPages[i].Upload(); } } } #endregion #region Protected Methods /// public void Dispose() { Clear(); if (tempFile != null) { tempFile.Dispose(); tempFile = null; if (File.Exists(tempFileName)) File.Delete(tempFileName); } BlobStore.Dispose(); } #endregion #region Public Methods internal void SetReport(Report report) { this.report = report; } /// /// Adds a source page to the prepared pages dictionary. /// /// The template page to add. /// /// Call this method before using AddPage and AddBand methods. This method adds /// a page to the dictionary that will be used to decrease size of the prepared report. /// public void AddSourcePage(ReportPage page) { SourcePages.Add(page); } /// /// Adds a new page. /// /// The original (template) page to add. /// /// Call the method before adding a page. This method creates /// a new output page with settings based on page parameter. /// public void AddPage(ReportPage page) { CurPage++; if (CurPage >= Count || AddPageAction != AddPageAction.WriteOver) { PreparedPage preparedPage = new PreparedPage(page, this); preparedPages.Add(preparedPage); // upload previous page to the file cache if enabled if (CanUploadToCache && Count > 1) preparedPages[Count - 2].Upload(); AddPageAction = AddPageAction.WriteOver; CurPage = Count - 1; Report.Engine.IncLogicalPageNumber(); } } /// /// Prints a band with all its child objects. /// /// The band to print. /// true if band was printed; false if it can't be printed /// on current page due to its PrintOn property value. /// /// Call the method before adding a band. /// public bool AddBand(BandBase band) { return preparedPages[CurPage].AddBand(band); } /// /// Gets a page with specified index. /// /// Zero-based index of page. /// The page with specified index. public ReportPage GetPage(int index) { if (index >= 0 && index < preparedPages.Count) { macroValues["Page#"] = index + Report.InitialPageNumber; macroValues["TotalPages#"] = preparedPages.Count + Report.InitialPageNumber - 1; ReportPage page = preparedPages[index].GetPage(); if (page.MirrorMargins && (index + 1) % 2 == 0) { float f = page.LeftMargin; page.LeftMargin = page.RightMargin; page.RightMargin = f; } return page; } else return null; } internal PreparedPage GetPreparedPage(int index) { if (index >= 0 && index < preparedPages.Count) { macroValues["Page#"] = index + Report.InitialPageNumber; macroValues["TotalPages#"] = preparedPages.Count + Report.InitialPageNumber - 1; return preparedPages[index]; } else return null; } internal ReportPage GetCachedPage(int index) { return pageCache.Get(index); } /// /// Gets the size of specified page, in pixels. /// /// Index of page. /// the size of specified page, in pixels. public SizeF GetPageSize(int index) { return preparedPages[index].PageSize; } /// /// Replaces the prepared page with specified one. /// /// The index of prepared page to replace. /// The new page to replace with. public void ModifyPage(int index, ReportPage newPage) { PreparedPage preparedPage = new PreparedPage(newPage, this); foreach (Base obj in newPage.ChildObjects) { if (obj is BandBase) preparedPage.AddBand(obj as BandBase); } preparedPages[index].Dispose(); preparedPages[index] = preparedPage; pageCache.Remove(index); } /// /// Modify the prepared page with new sizes. /// /// The name of prepared page to reSize. public void ModifyPageSize(string name) { foreach (PreparedPage page in preparedPages) { if (String.Equals(name, page.GetName(), StringComparison.InvariantCultureIgnoreCase)) { page.ReCalcSizes(); } } } /// /// Removes a page with the specified index. /// /// The zero-based index of page to remove. public void RemovePage(int index) { preparedPages[index].Dispose(); preparedPages.RemoveAt(index); pageCache.Clear(); } /// /// Creates a copy of a page with specified index and inserts it after original one. /// /// The zero-based index of original page. public void CopyPage(int index) { // insert a new empty page at specified index PreparedPage newPage = new PreparedPage(null, this); if (index == preparedPages.Count - 1) preparedPages.Add(newPage); else preparedPages.Insert(index + 1, newPage); // and copy source page into it ModifyPage(index + 1, GetPage(index)); pageCache.Clear(); } internal void InterleaveWithBackPage(int backPageIndex) { PreparedPage page = preparedPages[backPageIndex]; int count = backPageIndex - 1; for (int i = 0; i < count; i++) { preparedPages.Insert(i * 2 + 1, page); } } internal void ApplyWatermark(Watermark watermark) { SourcePages.ApplyWatermark(watermark); pageCache.Clear(); } internal void CutObjects(int index) { cutObjects = preparedPages[CurPage].CutObjects(index); } internal void PasteObjects(float x, float y) { preparedPages[CurPage].PasteObjects(cutObjects, x, y); } internal float GetLastY() { return preparedPages[CurPage].GetLastY(); } internal void PrepareToFirstPass() { firstPassPage = CurPage; firstPassPosition = CurPosition; Outline.PrepareToFirstPass(); } internal void ClearFirstPass() { Bookmarks.ClearFirstPass(); Outline.ClearFirstPass(); // clear all pages after specified FFirstPassPage while (firstPassPage < Count - 1) { RemovePage(Count - 1); } // if position is at begin, clear all pages if (firstPassPage == 0 && firstPassPosition == 0) RemovePage(0); // delete objects on the FFirstPassPage if (firstPassPage >= 0 && firstPassPage < Count) preparedPages[firstPassPage].CutObjects(firstPassPosition).Dispose(); CurPage = firstPassPage; } internal bool ContainsBand(Type bandType) { return preparedPages[CurPage].ContainsBand(bandType); } internal bool ContainsBand(string bandName) { return preparedPages[CurPage].ContainsBand(bandName); } /// /// Saves prepared pages to a stream. /// /// The stream to save to. public void Save(Stream stream) { if (Config.PreparedCompressed) stream = Compressor.Compress(stream); using (XmlDocument doc = new XmlDocument()) { doc.AutoIndent = true; doc.Root.Name = "preparedreport"; // save ReportInfo doc.Root.SetProp("ReportInfo.Name", Report.ReportInfo.Name); doc.Root.SetProp("ReportInfo.Author", Report.ReportInfo.Author); doc.Root.SetProp("ReportInfo.Description", Report.ReportInfo.Description); doc.Root.SetProp("ReportInfo.Created", SystemFake.DateTime.Now.ToString()); doc.Root.SetProp("ReportInfo.Modified", SystemFake.DateTime.Now.ToString()); doc.Root.SetProp("ReportInfo.CreatorVersion", Report.ReportInfo.CreatorVersion); XmlItem pages = doc.Root.Add(); pages.Name = "pages"; // attach each page's xml to the doc foreach (PreparedPage page in preparedPages) { page.Load(); pages.AddItem(page.Xml); } XmlItem sourcePages = doc.Root.Add(); sourcePages.Name = "sourcepages"; SourcePages.Save(sourcePages); XmlItem dictionary = doc.Root.Add(); dictionary.Name = "dictionary"; Dictionary.Save(dictionary); XmlItem bookmarks = doc.Root.Add(); bookmarks.Name = "bookmarks"; Bookmarks.Save(bookmarks); doc.Root.AddItem(Outline.Xml); XmlItem blobStore = doc.Root.Add(); blobStore.Name = "blobstore"; BlobStore.Save(blobStore); doc.Save(stream); // detach each page's xml from the doc foreach (PreparedPage page in preparedPages) { page.Xml.Parent = null; page.ClearUploadedXml(); } Outline.Xml.Parent = null; } if (Config.PreparedCompressed) stream.Dispose(); } /// /// Saves prepared pages to a .fpx file. /// /// The name of the file to save to. public void Save(string fileName) { using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { Save(stream); } } /// /// Loads prepared pages from a stream. /// /// The stream to load from. public void Load(Stream stream) { Clear(); if (stream.Length == 0) return; if (!stream.CanSeek) { MemoryStream tempStream = new MemoryStream(); const int BUFFER_SIZE = 32768; stream.CopyTo(tempStream, BUFFER_SIZE); tempStream.Position = 0; stream = tempStream; } bool compressed = Compressor.IsStreamCompressed(stream); if (compressed) stream = Compressor.Decompress(stream, false); try { using (XmlDocument doc = new XmlDocument()) { doc.Load(stream); XmlItem sourcePages = doc.Root.FindItem("sourcepages"); SourcePages.Load(sourcePages); XmlItem dictionary = doc.Root.FindItem("dictionary"); Dictionary.Load(dictionary); XmlItem bookmarks = doc.Root.FindItem("bookmarks"); Bookmarks.Load(bookmarks); XmlItem outline = doc.Root.FindItem("outline"); Outline.Xml = outline; XmlItem blobStore = doc.Root.FindItem("blobstore"); BlobStore.LoadDestructive(blobStore); XmlItem pages = doc.Root.FindItem("pages"); while (pages.Count > 0) { XmlItem pageItem = pages[0]; PreparedPage preparedPage = new PreparedPage(null, this); preparedPages.Add(preparedPage); preparedPage.Xml = pageItem; } // load ReportInfo Report.ReportInfo.Name = doc.Root.GetProp("ReportInfo.Name"); Report.ReportInfo.Author = doc.Root.GetProp("ReportInfo.Author"); Report.ReportInfo.Description = doc.Root.GetProp("ReportInfo.Description"); DateTime createDate; if (DateTime.TryParse(doc.Root.GetProp("ReportInfo.Created"), out createDate)) Report.ReportInfo.Created = createDate; if (DateTime.TryParse(doc.Root.GetProp("ReportInfo.Modified"), out createDate)) Report.ReportInfo.Modified = createDate; Report.ReportInfo.CreatorVersion = doc.Root.GetProp("ReportInfo.CreatorVersion"); } } finally { if (compressed) stream.Dispose(); } } /// /// Loads prepared pages from a .fpx file. /// /// The name of the file to load from. public void Load(string fileName) { using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { Load(stream); } } /// /// Clears the prepared report's pages. /// public void Clear() { sourcePages.Clear(); pageCache.Clear(); foreach (PreparedPage page in preparedPages) { page.Dispose(); } preparedPages.Clear(); dictionary.Clear(); bookmarks.Clear(); outline.Clear(); blobStore.Clear(); curPage = 0; } internal void ClearPageCache() { pageCache.Clear(); } #endregion /// /// Creates the pages of a prepared report /// /// public PreparedPages(Report report) { this.report = report; sourcePages = new SourcePages(this); preparedPages = new List(); dictionary = new Dictionary(this); bookmarks = new Bookmarks(); outline = new Outline(); blobStore = new BlobStore(report != null ? report.UseFileCache : false); pageCache = new PageCache(this); macroValues = new Dictionary(); if (this.report != null && this.report.UseFileCache) { tempFileName = Config.CreateTempFile(""); tempFile = new FileStream(tempFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); } } } }