using FastReport.Data;
using FastReport.Preview;
using FastReport.Utils;
using System;
using System.Collections.Generic;
namespace FastReport.Engine
{
///
/// Represents the report engine.
///
public partial class ReportEngine
{
#region Fields
private Report report;
private float curX;
private float originX;
private float curY;
private int curColumn;
private BandBase curBand;
private DateTime date;
private int hierarchyLevel;
private float hierarchyIndent;
private string hierarchyRowNo;
private bool finalPass;
private int firstReportPage;
private bool isFirstReportPage;
private int pagesLimit = 0;
#endregion Fields
#region Properties
private Report Report
{
get { return report; }
}
private PreparedPages PreparedPages
{
get { return report.PreparedPages; }
}
///
/// Gets or sets the current X offset.
///
///
/// This property specifies the X offset where the current band will be printed.
///
public float CurX
{
get { return curX; }
set { curX = Converter.DecreasePrecision(value, 2); }
}
///
/// Gets or sets the current Y offset.
///
///
/// This property specifies the Y offset where the current band will be printed.
/// After the band is printed, this value is incremented by the band's height.
///
public float CurY
{
get { return curY; }
set { curY = Converter.DecreasePrecision(value, 2); }
}
///
/// Gets the index of currently printing column in the multi-column report.
///
///
/// This value is 0-based.
///
public int CurColumn
{
get { return curColumn; }
}
///
/// Gets or sets index of current prepared page the current band will print on.
///
///
/// Note: the page with specified index must exists. This property is used to print side-by-side
/// subreports and Table object. Usually you don't need to use it.
///
public int CurPage
{
get { return PreparedPages.CurPage; }
set { PreparedPages.CurPage = value; }
}
///
/// Gets the current page width, in pixels.
///
///
/// This property returns a paper width minus left and right margins.
///
public float PageWidth
{
get { return page.WidthInPixels - (page.LeftMargin + page.RightMargin) * Units.Millimeters; }
}
///
/// Gets the current page height, in pixels.
///
///
/// This property returns a paper height minus top and bottom margins.
///
public float PageHeight
{
get { return page.HeightInPixels - (page.TopMargin + page.BottomMargin) * Units.Millimeters; }
}
///
/// Gets the value indicating whether the page has unlimited height.
///
public bool UnlimitedHeight
{
get { return page.UnlimitedHeight; }
}
///
/// Gets the value indicating whether the page has unlimited width.
///
public bool UnlimitedWidth
{
get { return page.UnlimitedWidth; }
}
///
/// Gets or sets the current height of unlimited page.
///
public float UnlimitedHeightValue
{
get { return page.UnlimitedHeightValue; }
set { page.UnlimitedHeightValue = value; }
}
///
/// Gets or sets the current width of unlimited page.
///
public float UnlimitedWidthValue
{
get { return page.UnlimitedWidthValue; }
set { page.UnlimitedWidthValue = value; }
}
///
/// Gets the height of page footer (including all its child bands), in pixels.
///
public float PageFooterHeight
{
get { return GetBandHeightWithChildren(page.PageFooter); }
}
///
/// Gets the height of column footer (including all its child bands), in pixels.
///
public float ColumnFooterHeight
{
get { return GetBandHeightWithChildren(page.ColumnFooter); }
}
///
/// Gets the free space on the current page, in pixels.
///
///
/// This property returns the page height minus footers height minus CurY value.
///
public float FreeSpace
{
get
{
float pageHeight = PageHeight;
pageHeight -= PageFooterHeight;
pageHeight -= ColumnFooterHeight;
pageHeight -= GetFootersHeight();
return Converter.DecreasePrecision(pageHeight - CurY, 2);
}
}
///
/// Gets the current prepared page number.
///
///
/// This value is 1-based. The initial value (usually 1) is set in the Report.InitialPageNumber property.
///
public int PageNo
{
get { return GetLogicalPageNumber(); }
}
///
/// Gets the number of total pages in a prepared report.
///
///
/// To use this property, your report must be two-pass. Set the
/// property to true.
///
public int TotalPages
{
get { return GetLogicalTotalPages(); }
}
///
/// Gets the string that represents the current page number.
///
///
/// This property returns a locale-based value, for example: "Page 1".
///
public string PageN
{
get { return String.Format(Res.Get("Misc,PageN"), PageNo); }
}
///
/// Gets the string that represents the "Page N of M" number.
///
///
/// This property returns a locale-based value, for example: "Page 1 of 10".
///
public string PageNofM
{
get { return String.Format(Res.Get("Misc,PageNofM"), PageNo, TotalPages); }
}
///
/// Gets the current row number of currently printing band.
///
///
/// This value is 1-based. It resets to 1 on each new group.
///
public int RowNo
{
get { return curBand == null ? 0 : curBand.RowNo; }
}
///
/// Gets the running current row number of currently printing band.
///
///
/// This value is 1-based.
///
public int AbsRowNo
{
get { return curBand == null ? 0 : curBand.AbsRowNo; }
}
///
/// Gets the date of report start.
///
public DateTime Date
{
get { return date; }
}
///
/// Gets a value indicating whether the report is executing the final pass.
///
///
/// This property is true if report is one-pass, or if report is two-pass and
/// the second pass is executing.
///
public bool FinalPass
{
get { return finalPass; }
}
///
/// Gets a value indicating whether the report is executing the first pass.
///
///
/// This property is true if report is one-pass, or if report is two-pass and
/// the first pass is executing.
///
public bool FirstPass
{
get { return !(Report.DoublePass && FinalPass); }
}
///
/// Gets a level of hierarchy when printing hierarchical bands.
///
///
/// The first level of hierarchy has 0 index.
///
public int HierarchyLevel
{
get { return hierarchyLevel; }
}
///
/// Gets the row number like "1.2.1" when printing hierarchical bands.
///
public string HierarchyRowNo
{
get { return hierarchyRowNo; }
}
#endregion Properties
#region Constructors
internal ReportEngine(Report report)
{
this.report = report;
objectsToProcess = new List();
}
#endregion Constructors
#region Private Methods
private void ResetDesigningFlag()
{
ObjectCollection allObjects = Report.AllObjects;
foreach (Base c in allObjects)
{
c.SetDesigning(false);
}
}
private void InitializeData()
{
foreach (Base c in Report.Dictionary.AllObjects)
{
if (c is DataComponentBase)
(c as DataComponentBase).InitializeComponent();
}
foreach (Base c in Report.AllObjects)
{
if (c is ReportComponentBase obj)
obj.ResetData();
}
}
private void InitializeSecondPassData()
{
foreach (Base c in Report.Dictionary.AllObjects)
{
if (c is DataSourceBase)
{
DataSourceBase data = c as DataSourceBase;
if (data.RowCount > 0)
data.First();
}
}
}
private void PrepareToFirstPass(bool append)
{
finalPass = !Report.DoublePass;
if (!append)
PreparedPages.Clear();
else
PreparedPages.CurPage = PreparedPages.Count > 0 ? PreparedPages.Count - 1 : 0;
isFirstReportPage = true;
hierarchyLevel = 1;
PreparedPages.PrepareToFirstPass();
Report.Dictionary.Totals.ClearValues();
objectsToProcess.Clear();
InitializePages();
InitPageNumbers();
}
private void PrepareToSecondPass()
{
Report.Dictionary.Totals.ClearValues();
objectsToProcess.Clear();
PreparedPages.ClearFirstPass();
InitializeSecondPassData();
}
private float GetBandHeightWithChildren(BandBase band)
{
float result = 0;
while (band != null)
{
if (CanPrint(band))
result += (band.CanGrow || band.CanShrink) ? CalcHeight(band) : band.Height;
else if (FinalPass && !String.IsNullOrEmpty(band.VisibleExpression) && band.VisibleExpression.Contains("TotalPages"))
result += (band.CanGrow || band.CanShrink) ? CalcHeight(band) : band.Height;
band = band.Child;
if (band != null && ((band as ChildBand).FillUnusedSpace || (band as ChildBand).CompleteToNRows != 0))
break;
}
return result;
}
private void RunReportPages(ReportPage page)
{
OnStateChanged(Report, EngineState.ReportStarted);
if (page == null)
RunReportPages();
else
RunReportPage(page);
OnStateChanged(Report, EngineState.ReportFinished);
}
private void LimitPreparedPages()
{
// limit the prepared pages
if (Report.MaxPages > 0)
{
while (PreparedPages.Count > Report.MaxPages)
{
PreparedPages.RemovePage(PreparedPages.Count - 1);
}
}
// Limit the prepared pages again because pagesLimit has better priority than Report.MaxPages.
if (pagesLimit > 0)
{
while (PreparedPages.Count > pagesLimit)
{
PreparedPages.RemovePage(PreparedPages.Count - 1);
}
}
}
#endregion Private Methods
#region Internal Methods
internal bool Run(bool runDialogs, bool append, bool resetDataState)
{
return Run(runDialogs, append, resetDataState, null);
}
internal bool Run(bool runDialogs, bool append, bool resetDataState, int pagesLimit)
{
this.pagesLimit = pagesLimit;
return Run(runDialogs, append, resetDataState, null);
}
internal bool Run(bool runDialogs, bool append, bool resetDataState, ReportPage page)
{
RunPhase1(resetDataState);
try
{
if (runDialogs && !RunDialogs())
return false;
RunPhase2(append, page);
}
finally
{
RunFinished();
}
return true;
}
internal void RunPhase1(bool resetDataState = true, bool webDialog = false)
{
date = SystemFake.DateTime.Now;
Report.SetOperation(ReportOperation.Running);
ResetDesigningFlag();
// don't reset the data state if we run the hyperlink's detail page or refresh a report.
// This is necessary to keep data filtering settings alive
if (resetDataState)
InitializeData();
// don't call OnStartReport event again, if it's web dialog re-render
if (!webDialog)
Report.OnStartReport(EventArgs.Empty);
}
internal void RunPhase2(bool append, ReportPage page)
{
Config.ReportSettings.OnStartProgress(Report);
PrepareToFirstPass(append);
RunReportPages(page);
ResetLogicalPageNumber();
if (Report.DoublePass && !Report.Aborted)
{
finalPass = true;
PrepareToSecondPass();
RunReportPages(page);
}
}
internal void RunPhase2(int? pagesLimit = null)
{
if (pagesLimit != null)
this.pagesLimit = pagesLimit.Value;
try
{
RunPhase2(false, null);
}
finally
{
RunFinished();
}
}
internal void RunFinished()
{
Report.OnFinishReport(EventArgs.Empty);
Config.ReportSettings.OnFinishProgress(Report);
Report.SetOperation(ReportOperation.None);
LimitPreparedPages();
}
#endregion Internal Methods
}
}