337 lines
15 KiB
C#
337 lines
15 KiB
C#
using Sarand.BusinessMgr.Net.Standard.Base.App;
|
|
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
|
|
using Sarand.BusinessMgr.Net.Standard.Base.Procs;
|
|
using Sarand.Net.Standard.Base.Core;
|
|
using Sarand.Net.Standard.Base.CodeTypes;
|
|
using Sarand.Net.Standard.Base.Dbas;
|
|
using Sarand.Net.Standard.Base.Procs;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Drawing.Printing;
|
|
using System.Drawing.Text;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Sarand.Net.Standard.Base.App;
|
|
using Sarand.Net.Standard.Base.Utils;
|
|
using Sarand.BusinessMgr.Net.Standard.Base.Core;
|
|
using System.Runtime.Versioning;
|
|
|
|
namespace Sarand.BusinessMgr.Net.Core.Procs.Reports
|
|
{
|
|
//=======================================================================
|
|
[SupportedOSPlatform("windows")]
|
|
public abstract class SbmReport : SbmReport_Base
|
|
{
|
|
//-------------------------------------------------------------------
|
|
// Definitions.
|
|
public enum PrintPageResult
|
|
{
|
|
HasMorePages,
|
|
NoMorePages,
|
|
}
|
|
protected class PrintPageConfig
|
|
{
|
|
public PrintPageConfig(Graphics graphics, PageSettings pageSettings, Rectangle pageBounds, Rectangle marginBounds, int pageNumber, int pageCount)
|
|
{
|
|
// Graphics for printing to physical device (printer or print preview control).
|
|
Graphics = graphics;
|
|
// Printing configuration.
|
|
PageSettings = pageSettings;
|
|
PageBounds = pageBounds;
|
|
Bounds = marginBounds;
|
|
PixelsPerInch = new SizeF(Graphics.DpiX, Graphics.DpiY);
|
|
TabX = (int)Math.Ceiling(Bounds.Width * 0.025f);
|
|
PageNumber = pageNumber;
|
|
}
|
|
/// <summary>Current graphics for printing on page.</summary>
|
|
public Graphics Graphics { get; private set; }
|
|
/// <summary>Current page number</summary>
|
|
public int PageNumber { get; }
|
|
/// <summary>Virtual page number</summary>
|
|
public int PageNumberVirtual { get; set; } = 1;
|
|
/// <summary>Total number of pages</summary>
|
|
public int PageCount { get; }
|
|
/// <summary>Page settings from PrintPage event - eg. to modify page length for continuous feed.</summary>
|
|
public PageSettings PageSettings { get; }
|
|
/// <summary>Page bounds from PrintPage event - eg. to modify page length for continuous feed.</summary>
|
|
public Rectangle PageBounds { get; }
|
|
// Page bounds.
|
|
public Rectangle Bounds { get; set; }
|
|
// Pixel sizes.
|
|
public SizeF PixelsPerInch { get; }
|
|
// Horizontal printing positions.
|
|
public int PosXLeft { get { return Bounds.Left; } }
|
|
public int PosXCenter { get { return (int)(Bounds.Left + (Bounds.Width * 0.5f)); } }
|
|
public int PosXRight { get { return Bounds.Right; } }
|
|
public int TabX { get; set; }
|
|
public int[] ColsX { get; set; }
|
|
// Current vertical print position.
|
|
public int PosY { get; set; }
|
|
public int PosYBodyTop { get; set; }
|
|
public int PosYBodyBottom { get; set; }
|
|
public int PosYFooterTop { get; set; }
|
|
// Line spacing - proportional to font size.
|
|
public float HeadingTextLineSpacing { get; set; } = 1.25f;
|
|
public float IntroTextLineSpacing { get; set; } = 1.20f;
|
|
public float DetailTextLineSpacing { get; set; } = 1.15f;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Configuration.
|
|
public new class Config : SbmReport_Base.Config
|
|
{
|
|
protected Config(ISbmUserOrganisationContext userContext) :
|
|
base(userContext)
|
|
{
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Data members.
|
|
protected SbmSystem system = SbmGlobals.System;
|
|
private Config config;
|
|
/// <summary>Print mode controls whether physical printing to printer or preview control or to dummy device to determine page size.</summary>
|
|
private enum PrintMode { Physical, Virtual }
|
|
private PrintMode printMode = PrintMode.Physical;
|
|
// Printing tools.
|
|
private StringFormat stringFormat = StringFormat.GenericTypographic;
|
|
private static ColorConverter colorConverter = new ColorConverter();
|
|
private SbmPrinter_Footer footerPrinter = new SbmPrinter_Footer();
|
|
protected Brush brushBlack = new SolidBrush(Color.Black);
|
|
|
|
//-------------------------------------------------------------------
|
|
// Construction.
|
|
protected SbmReport(Config config) : base(config)
|
|
{
|
|
this.config = config;
|
|
}
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
stringFormat.Dispose();
|
|
footerPrinter.Dispose();
|
|
brushBlack.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Properties.
|
|
public static String FontFamilyName { get; } = "Arial";
|
|
public static Color PrintColor { get; } = Color.Black;
|
|
protected StringFormat StringFormat { get { return stringFormat; } }
|
|
protected static ColorConverter ColorConverter { get { return colorConverter; } }
|
|
// Report printing progress.
|
|
/// <summary>Check if currently printing to the preview device.</summary>
|
|
protected bool PreviewPrinting { get; private set; }
|
|
/// <summary>Check if the current print mode is 'Physical', ie. actually printing to the page.</summary>
|
|
protected bool PhysicalPrinting { get { return printMode == PrintMode.Physical; } }
|
|
/// <summary>Check if currently printing to file.</summary>
|
|
protected bool FilePrinting { get; private set; }
|
|
/// <summary>Total number of items to be printed</summary>
|
|
public virtual int ItemCount { get; protected set; }
|
|
/// <summary>Position of item currently being published in the list.</summary>
|
|
public virtual int ItemNumber { get; protected set; }
|
|
/// <summary>Progress based on the item being printed against the total number to be printed.</summary>
|
|
public override decimal ProgressPercent { get { return (ItemCount > 0 ? (decimal)ItemNumber / ItemCount : 0); } }
|
|
|
|
//-------------------------------------------------------------------
|
|
// Text utilities.
|
|
protected SizeF DrawString(Graphics graphics, String text, Font font, Brush brush, PointF position, StringFormat format)
|
|
{
|
|
if (!VirtualPrint)
|
|
graphics.DrawString(text, font, brush, position, format);
|
|
// Calculate size of text.
|
|
SizeF sizeText = graphics.MeasureString(text, font);
|
|
return sizeText;
|
|
}
|
|
protected SizeF DrawString(Graphics graphics, String text, Font font, Brush brush, RectangleF position, StringFormat format)
|
|
{
|
|
if (!VirtualPrint)
|
|
graphics.DrawString(text, font, brush, position, format);
|
|
// For single lines of text, the height can be different for Physical (printing) and
|
|
// Virtual (dummy) graphics even though they appear to be all but identical.
|
|
// Closest issue description: https://social.msdn.microsoft.com/Forums/windows/en-US/7a431b8c-3ad5-49d8-b012-f56a21c5cec5/measurestring-returns-different-height-after-text-width-exceeds-layout-width?forum=winforms
|
|
SizeF sizeText = graphics.MeasureString(text, font, (int)position.Width, format);
|
|
if (sizeText.Height < font.GetHeight(graphics))
|
|
// Aug-2018 Temporary fix.
|
|
sizeText.Height = font.GetHeight(graphics);
|
|
return sizeText;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Printer configuration.
|
|
public abstract void ConfigurePrinter(PageSettings pageSettings);
|
|
public abstract void ConfigurePrintPreview(PageSettings pageSettings);
|
|
public abstract void ConfigurePageMargins(PageSettings pageSettings);
|
|
protected abstract void InitDrawingTools(Graphics graphics, Rectangle bounds);
|
|
|
|
//-------------------------------------------------------------------
|
|
// P r i n t P a g e S e c t i o n
|
|
//-------------------------------------------------------------------
|
|
// Solution to how to determine the number of pages in a report and
|
|
// print the report across multiple pages even though all printing is
|
|
// done via PrintPage() which prints a single page at a time.
|
|
/// <summary>
|
|
/// Print page section - portion of the report that is printed as a contiguous section, ie. all on one page.
|
|
/// <para>NB Page sections should be kept short for reports that may run over one page to ensure that the section doesn't overflow a page.</para>
|
|
/// <para>eg. A single row in a printed table with optional headings.</para>
|
|
/// <para>Method can be anonymous to allow other parameters to be passed in the context of the report's page printing, rathe rthan just relying on the Page Config.</para>
|
|
/// </summary>
|
|
protected delegate void PrintPageSectionFn();
|
|
/// <summary> Print mode is either physical - printing to printer or preview control - or virtual - to test whether section fits on page. </summary>
|
|
protected bool VirtualPrint { get { return printMode == PrintMode.Virtual; } }
|
|
private void SwitchPrintMode(PrintMode mode, PrintPageConfig cfg)
|
|
{
|
|
printMode = mode;
|
|
}
|
|
/// <summary>
|
|
/// Test print the page section to verify that it fits on the page, before printing to the physical device.
|
|
/// <para>Returns false if section doesn't fit on the page.</para>
|
|
/// </summary>
|
|
protected bool PrintPageSectionIfSpace(PrintPageConfig cfg, PrintPageSectionFn printPageSectionFn)
|
|
{
|
|
// Make a note of the current print position and test print the section to the virtual page.
|
|
int posYInitial = cfg.PosY;
|
|
SwitchPrintMode(PrintMode.Virtual, cfg);
|
|
printPageSectionFn();
|
|
// Check if the section fits on the page.
|
|
if (cfg.PosY >= cfg.PosYBodyBottom)
|
|
{
|
|
// If the virtual page is the same or greater (shouldn't happen)
|
|
// than the current page, a new page is required.
|
|
if (cfg.PageNumberVirtual >= cfg.PageNumber)
|
|
{
|
|
SwitchPrintMode(PrintMode.Physical, cfg);
|
|
return false;
|
|
}
|
|
// Otherwise, we can increment the virtual page and reset printing to the top before reprinting the section.
|
|
cfg.PageNumberVirtual++;
|
|
cfg.PosY = cfg.PosYBodyTop;
|
|
if (cfg.PageNumberVirtual == cfg.PageNumber)
|
|
SwitchPrintMode(PrintMode.Physical, cfg);
|
|
}
|
|
else
|
|
{
|
|
// Section fits on virtual page.
|
|
if (cfg.PageNumberVirtual != cfg.PageNumber)
|
|
// Not yet on current page so no need to reprint section.
|
|
return true;
|
|
// On current page and section needs to be printed at original position to the physical device.
|
|
cfg.PosY = posYInitial;
|
|
SwitchPrintMode(PrintMode.Physical, cfg);
|
|
}
|
|
printPageSectionFn();
|
|
SwitchPrintMode(PrintMode.Physical, cfg);
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// P r i n t i n g
|
|
//-------------------------------------------------------------------
|
|
public virtual void BeginPrint(PrintAction printAction, int pageNumberFrom = 0, int pageNumberTo = 0)
|
|
{
|
|
if (DebugMode) SaLogger.Debug($"{SysUtil.Fn} PrintAction: " + printAction + ". Page range: " + pageNumberFrom + "-" + pageNumberTo + ". Page count: " + PageCount);
|
|
PageNumberFrom = PageNumberFrom;
|
|
PageNumberTo = pageNumberTo;
|
|
PageNumber = Math.Max(1, pageNumberFrom) - 1;
|
|
ItemCount = 0;
|
|
ItemNumber = 0;
|
|
PreviewPrinting = printAction == PrintAction.PrintToPreview;
|
|
FilePrinting = printAction == PrintAction.PrintToFile;
|
|
}
|
|
public virtual void QueryPageSettings(PageSettings pageSettings, PrintAction printAction) { }
|
|
public PrintPageResult PrintPage(Graphics graphics, PageSettings pageSettings, Rectangle pageBounds, Rectangle marginBounds)
|
|
{
|
|
PageNumber++;
|
|
|
|
// Configure graphics for printing document.
|
|
// NB Graphics properties set here muust also be copied to virtual device graphics in PrintPageConfig above.
|
|
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
|
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
|
|
InitDrawingTools(graphics, marginBounds);
|
|
// Configure default String Format.
|
|
StringFormat.Alignment = StringAlignment.Near;
|
|
StringFormat.LineAlignment = StringAlignment.Near;
|
|
StringFormat.FormatFlags = StringFormatFlags.FitBlackBox | StringFormatFlags.LineLimit | StringFormatFlags.NoClip /*| StringFormatFlags.NoWrap */;
|
|
StringFormat.Trimming = StringTrimming.Character;
|
|
|
|
// Page configuration.
|
|
PrintPageConfig cfg = new PrintPageConfig(graphics, pageSettings, pageBounds, marginBounds, PageNumber, PageCount);
|
|
cfg.Bounds = marginBounds;
|
|
cfg.PosY = marginBounds.Top;
|
|
cfg.PosYBodyTop = marginBounds.Top;
|
|
cfg.PosYBodyBottom = marginBounds.Bottom;
|
|
if (DebugMode) SaLogger.Debug($"{SysUtil.Fn} Page: " + PageNumber);
|
|
SwitchPrintMode(PrintMode.Physical, cfg);
|
|
|
|
// Log print configuration.
|
|
if (SbmGlobals.System.IsExtendedLogging(SbmSystem.ExtendedLogginOption.Printing))
|
|
LogPageSettings(graphics, pageSettings, pageBounds, marginBounds);
|
|
|
|
// Print page and interpret result.
|
|
PrintPageResult printPageResult = PrintPage(cfg);
|
|
// Update page range information depending on whether this is the last page in the report.
|
|
if (printPageResult == PrintPageResult.HasMorePages)
|
|
{
|
|
PageNumberMaxKnown = Math.Max(PageNumberMaxKnown, PageNumber + 1);
|
|
}
|
|
else if (printPageResult == PrintPageResult.NoMorePages)
|
|
{
|
|
PageNumberMaxKnown = PageNumber;
|
|
PageCount = PageNumber;
|
|
}
|
|
// Always return 'No more pages' if finished printing page range.
|
|
if (PageNumberTo > 0 && PageNumber + 1 > PageNumberTo) return PrintPageResult.NoMorePages;
|
|
return printPageResult;
|
|
}
|
|
protected abstract PrintPageResult PrintPage(PrintPageConfig cfg);
|
|
public virtual void EndPrint(PrintAction printAction) { }
|
|
|
|
//-------------------------------------------------------------------
|
|
protected void LogPageSettings(Graphics graphics, PageSettings pageSettings, Rectangle pageBounds, Rectangle marginBounds)
|
|
{
|
|
// Log print configuration.
|
|
List<StringPair> info = new List<StringPair>();
|
|
// Color=True, Landscape=False, Margins=[Margins Left=36 Right=36 Top=36 Bottom=36], PaperSize=[PaperSize Custom Kind=Custom Height=3600 Width=1800], PaperSource=[PaperSource unknown Kind=FormSource], PrinterResolution=[PrinterResolution X=600 Y=600]]
|
|
info.Add("Bounds", pageSettings.Bounds.ToString());
|
|
info.Add("Color", pageSettings.Color.ToString());
|
|
info.Add("Landscape", pageSettings.Landscape.ToString());
|
|
info.Add("Margins", pageSettings.Margins.ToString());
|
|
info.Add("PaperSize", pageSettings.PaperSize.ToString());
|
|
info.Add("PaperSource", pageSettings.PaperSource.ToString());
|
|
info.Add("PrintableArea", pageSettings.PrintableArea.ToString());
|
|
info.Add("PrinterResolution", pageSettings.PrinterResolution.ToString());
|
|
SaLogger.Info(GetType().Name + "Report: " + Caption + ". Page settings" + (PreviewPrinting ? " [preview]:" : "\n\tPrinter: " + pageSettings.PrinterSettings.PrinterName) + "\n\tPage bounds: " + pageBounds + "\n\tMargin bounds: " + marginBounds + "\n\tPage settings:" + info.Concat(": ").Concat("", "\n\t\t"), true);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// H e a d e r a n d F o o t e r
|
|
//-------------------------------------------------------------------
|
|
protected virtual void PrintHeader(PrintPageConfig cfg)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Printing - Footer.
|
|
protected virtual void PrintFooter(PrintPageConfig cfg)
|
|
{
|
|
// Print footer - Format.
|
|
SbmPrinter_Footer.Config footerConfig = new SbmPrinter_Footer.Config();
|
|
footerConfig.Graphics = cfg.Graphics;
|
|
footerConfig.PrintArea = cfg.Bounds;
|
|
footerConfig.PosY = cfg.PosYFooterTop;
|
|
footerConfig.PageNumber = PageNumber;
|
|
footerConfig.PageCount = PageCount;
|
|
footerPrinter.PrintFooter(footerConfig);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
}
|