/// /// Copyright (c) 2021-2023 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Printing; using System.IO; using Common; using Results.Resources; namespace Results.Output.Printers.Zapiska { public class ZapiskaPrintDocument : PrintDocument { const float GapBetweenTableColumns = 10.0f; const float GapBetweenCommonColumns = 10.0f; /// Size of the printed area without margins, after taking into account 'pageOrientation' readonly int printHeight; /// in 0.254 mm = 0.01 in readonly int printWidth; /// in 0.254 mm = 0.01 in readonly int topMargin; /// in 0.254 mm = 0.01 in readonly int bottomMargin; /// in 0.254 mm = 0.01 in readonly int leftMargin; /// in 0.254 mm = 0.01 in readonly float leftMarginCommon1; readonly float leftMarginCommon2; readonly float leftMarginCommon3; readonly float leftMarginBelow1; readonly float leftMarginBelow2; readonly float leftMarginBelow3; readonly float spacingOne; readonly float spacingOne4Header; float commonTop; /// = TitleY + 60 float bodyTop; /// = HdrTop + 160 /// float titleHeight; float commonHeight; /// /// Property variable for the Font the user wishes to use /// Font titleFont; Font headerFont; Font font; Font footerFont; /// /// Data to print /// Results.Entities.Batch batch; ZapiskaPrinterCfg cfg; /// /// Items to print /// readonly string header; readonly IList commonItems1; readonly IList commonItems2; readonly IList commonItems3; readonly IList selectedItems; readonly IList belowItems1; readonly IList belowItems2; readonly IList belowItems3; readonly string footer; /// /// Layout and status /// int tablesOnFirstPage; int tablesOnNextPage; int nrPages; int pageNr; /// Page number to be printed at the bottom of the page /// /// Constructor /// /// Text to be printed public ZapiskaPrintDocument(Results.Entities.Batch batch, ZapiskaPrinterCfg cfg, string documentName) { this.batch = batch; DocumentName = documentName; this.cfg = cfg; topMargin = cfg.TopMargin; bottomMargin = cfg.BottomMargin; leftMargin = cfg.LeftMargin; header = string.IsNullOrEmpty(cfg.Header) ? string.Empty : cfg.Header.Replace("~", Environment.NewLine); commonItems1 = WMeterRsltItemSpec.FromStrArray(cfg.CommonItems1); commonItems2 = WMeterRsltItemSpec.FromStrArray(cfg.CommonItems2); commonItems3 = WMeterRsltItemSpec.FromStrArray(cfg.CommonItems3); selectedItems = WMeterRsltItemSpec.FromStrArray(cfg.SelectedItems); belowItems1 = WMeterRsltItemSpec.FromStrArray(cfg.BelowItems1); belowItems2 = WMeterRsltItemSpec.FromStrArray(cfg.BelowItems2); belowItems3 = WMeterRsltItemSpec.FromStrArray(cfg.BelowItems3); footer = string.IsNullOrEmpty(cfg.Footer) ? string.Empty : cfg.Footer.Replace("~", Environment.NewLine); /// Initialize fonts titleFont = new Font(cfg.TitFntFml ?? "Arial", cfg.TitFntSz > 0 ? cfg.TitFntSz : 10, (FontStyle)cfg.TitFntSty); headerFont = new Font(cfg.HdrFntFml ?? "Arial", cfg.HdrFntSz > 0 ? cfg.HdrFntSz : 10, (FontStyle)cfg.HdrFntSty); font = new Font(cfg.BodyFntFml ?? "Arial", cfg.BodyFntSz > 0 ? cfg.BodyFntSz : 10, (FontStyle)cfg.BodyFntSty); footerFont = new Font(cfg.FooterFntFml ?? "Arial", cfg.FooterFntSz > 0 ? cfg.FooterFntSz : 10, (FontStyle)cfg.FooterFntSty); /// Set print area size and margins (in dots using 100 dpi) DefaultPageSettings.Landscape = (cfg.PageOrientation == PageOrientation.Landscape); /// if (DefaultPageSettings.Landscape) { printHeight = base.DefaultPageSettings.PaperSize.Width - topMargin - bottomMargin; printWidth = base.DefaultPageSettings.PaperSize.Height - leftMargin - leftMargin; } else { printHeight = base.DefaultPageSettings.PaperSize.Height - topMargin - bottomMargin; printWidth = base.DefaultPageSettings.PaperSize.Width - leftMargin - leftMargin; } leftMarginCommon1 = leftMargin + printWidth * 0.01f * cfg.ColW0Pct; leftMarginCommon2 = leftMargin + printWidth * 0.01f * (cfg.ColW0Pct + cfg.ColW1Pct); leftMarginCommon3 = leftMargin + printWidth * 0.01f * (cfg.ColW0Pct + cfg.ColW1Pct + cfg.ColW2Pct); leftMarginBelow1 = leftMargin + printWidth * 0.01f * cfg.BelowW0Pct; leftMarginBelow2 = leftMargin + printWidth * 0.01f * (cfg.BelowW0Pct + cfg.BelowW1Pct); leftMarginBelow3 = leftMargin + printWidth * 0.01f * (cfg.BelowW0Pct + cfg.BelowW1Pct + cfg.BelowW2Pct); spacingOne = System.Windows.Forms.TextRenderer.MeasureText("Abcgq", font).Height; spacingOne4Header = System.Windows.Forms.TextRenderer.MeasureText("Abcgq", headerFont).Height; pageNr = 1; } /// /// Override the default onbeginPrint method of the PrintDocument Object /// /// /// protected override void OnBeginPrint(PrintEventArgs e) { base.OnBeginPrint(e); } /// /// Override the default OnPrintPage method of the PrintDocument. /// /// /// This provides the print logic for our document protected override void OnPrintPage(PrintPageEventArgs e) { /// Run base code base.OnPrintPage(e); if (pageNr == 1) { if (!string.IsNullOrEmpty(cfg.Template)) { string templatePath = Path.Combine("C:\\TBF\\Templates\\", cfg.Template); if (File.Exists(templatePath)) { if (cfg.PageOrientation == PageOrientation.Portrait) e.Graphics.DrawImage(new Bitmap(Image.FromFile(templatePath), 827, 1169), new Rectangle(0, 0, 827, 1169)); else e.Graphics.DrawImage(new Bitmap(Image.FromFile(templatePath), 1169, 827), new Rectangle(0, 0, 1169, 827)); } } /// Calculate the table rows count int tableRowsCount = 0; foreach (var wm in batch.WaterMeters) { if (!cfg.GoodOnly || wm.Passed) tableRowsCount++; } /// Prepare document outline titleHeight = System.Windows.Forms.TextRenderer.MeasureText(header, titleFont).Height; commonTop = cfg.TopMargin + titleHeight + spacingOne4Header; int commonCount = Math.Max(commonItems1.Count, Math.Max(commonItems2.Count, commonItems3.Count)); commonHeight = commonCount * spacingOne; bodyTop = commonTop + commonHeight + spacingOne4Header; float wmSectionHeight = (tableRowsCount + 1) * spacingOne + 3 * spacingOne4Header; tablesOnFirstPage = (int)Math.Floor((printHeight - bodyTop + (float)cfg.TopMargin - 2 * spacingOne) / wmSectionHeight); tablesOnNextPage = Math.Max(1, (int)Math.Floor((printHeight - 2 * spacingOne) / wmSectionHeight)); /// Min. 1, prevent division by zero if (tablesOnFirstPage >= 1) nrPages = 1; else nrPages = 2; } /// Use the StringFormat class for the text layout of our document StringFormat format = new StringFormat(StringFormatFlags.LineLimit); if (pageNr == 1) { ///---------- /// Title ///---------- float titleWid = e.Graphics.MeasureString(header, titleFont).Width; RectangleF printArea = new RectangleF(cfg.LeftMargin + Math.Max(0, (printWidth - titleWid)) / 2, cfg.TopMargin, titleWid, titleHeight); e.Graphics.DrawString(header, titleFont, Brushes.Black, printArea); ///---------------- /// Common items ///---------------- for (int n = 1; n <= 3; n++) { IList commonItems; float lMargin; switch (n) { default: case 1: commonItems = commonItems1; lMargin = leftMarginCommon1; break; case 2: commonItems = commonItems2; lMargin = leftMarginCommon2; break; case 3: commonItems = commonItems3; lMargin = leftMarginCommon3; break; } if (commonItems.Count == 0) continue; /// Common part is empty string[] leftColumn = new string[commonItems.Count]; string[] rightColumn = new string[commonItems.Count]; int cnt = 0; foreach (var v in commonItems) { leftColumn[cnt] = v.Caption; rightColumn[cnt] = (batch.WaterMeters.Count > 0) ? v.Print(batch.WaterMeters[0]) : string.Empty; cnt++; } /// Determine left column width float leftColWidth = 0; foreach (var paragraph in leftColumn) { string[] lines = paragraph.Split(new char[] { '~' }); foreach (var line in lines) { float itemWidth = Measure(e, paragraph, this.font).Width; if (itemWidth > leftColWidth) { leftColWidth = itemWidth; } } } /// Write aligned columns for (int i = 0; i < Math.Min(leftColumn.Length, rightColumn.Length); i++) { PrintAt(e, lMargin, commonTop + spacingOne * i, leftColWidth, leftColumn[i], this.font, commonItems[i].Alignment); PrintAt(e, lMargin + leftColWidth + GapBetweenCommonColumns, commonTop + spacingOne * i, 0, rightColumn[i], this.font, Alignment.Left); } } } ///-------- /// Body ///-------- /// This variable represents the top coordinate of the area where WM results are printed /// and it is updated (increased by) the value returned by PrintXyzWM() - the section size. float nextTableTop = (pageNr == 1) ? bodyTop : cfg.TopMargin; if (tablesOnFirstPage > 0 || pageNr == 2) { nextTableTop += PrintWMs(e, batch.WaterMeters, nextTableTop); } e.HasMorePages = (nrPages > 1) && (pageNr == 1); ///---------------- /// Below items ///---------------- for (int n = 1; n <= 3; n++) { IList belowItems; float lMargin; switch (n) { default: case 1: belowItems = belowItems1; lMargin = leftMarginBelow1; break; case 2: belowItems = belowItems2; lMargin = leftMarginBelow2; break; case 3: belowItems = belowItems3; lMargin = leftMarginBelow3; break; } if (belowItems.Count == 0) continue; /// Below part is empty string[] leftColumn = new string[belowItems.Count]; string[] rightColumn = new string[belowItems.Count]; int cnt = 0; foreach (var v in belowItems) { leftColumn[cnt] = v.Caption; rightColumn[cnt] = (batch.WaterMeters.Count > 0) ? v.Print(batch.WaterMeters[0]) : string.Empty; cnt++; } /// Determine left column width float leftColWidth = 0; foreach (var paragraph in leftColumn) { string[] lines = paragraph.Split(new char[] { '~' }); foreach (var line in lines) { float itemWidth = e.Graphics.MeasureString(paragraph, font).Width + 1; if (itemWidth > leftColWidth) leftColWidth = itemWidth; } } /// Write aligned columns for (int i = 0; i < Math.Min(leftColumn.Length, rightColumn.Length); i++) { PrintAt(e, lMargin, nextTableTop + spacingOne * i, leftColWidth, leftColumn[i], this.font, belowItems[i].Alignment); PrintAt(e, lMargin + leftColWidth + GapBetweenCommonColumns, nextTableTop + spacingOne * i, 0, rightColumn[i], this.font, Alignment.Left); } } ///---------- /// Footer ///---------- float footerWidth = Measure(e, footer, footerFont).Width; PrintAt(e, leftMargin + (printWidth - footerWidth) / 2, topMargin + printHeight - 2 * spacingOne, 0, footer, footerFont, Alignment.Left); if (nrPages != 1) { string pageNrText = string.Format("{0} {1}/{2}", Strings.Page, pageNr, nrPages); float pageNrWidth = e.Graphics.MeasureString(pageNrText, font).Width; PrintAt(e, leftMargin + (printWidth - pageNrWidth) / 2, topMargin + printHeight - spacingOne, 0, pageNrText, font, Alignment.Left); } pageNr++; } /// /// Print one water meter results /// /// Water meter /// Number of the water meter printed /// Top coordinate of watermeter data /// The height of the printed section float PrintWMs(PrintPageEventArgs e, IList wms, float top) { /// /// Prepare table content /// int tableColumnsCount = selectedItems.Count; /// string[] tableHeader = new string[tableColumnsCount]; IList tableRows = new List(); Alignment[] itemAlignment = new Alignment[tableColumnsCount]; /// for (int i = 0; i < tableColumnsCount; i++) { tableHeader[i] = selectedItems[i].Caption; itemAlignment[i] = selectedItems[i].Alignment; } /// WMeterRsltItemSpec.TableRowNr = 0; foreach (var wm in wms) { if (!cfg.GoodOnly || wm.Passed) { string[] oneTableRow = new string[tableColumnsCount]; WMeterRsltItemSpec.TableRowNr++; for (int i = 0; i < tableColumnsCount; i++) { WMeterRsltItemSpec.TableColumnNr = i + 1; oneTableRow[i] = selectedItems[i].Print(wm).Split(new char[] { '|' })[0]; /// Strip color information } tableRows.Add(oneTableRow); } } return DrawTable(e, leftMargin, top, tableHeader, tableRows, itemAlignment); } float DrawTable(PrintPageEventArgs e, float left, float top, string[] header, IList rows, Alignment[] alignment) { int tblColumnsCount = header.Length; float[] columnWidth = new float[tblColumnsCount]; float[] columnPos = new float[tblColumnsCount + 1]; columnPos[0] = left; /// /// Measure table column widths and header height /// float headerHeight = 0; for (int i = 0; i < tblColumnsCount; i++) { SizeF size = Measure(e, header[i], headerFont); columnWidth[i] = size.Width; if (size.Height > headerHeight) headerHeight = size.Height; foreach (var tableRow in rows) { float width = Measure(e, tableRow[i], font).Width + 1; if (width > columnWidth[i]) columnWidth[i] = width; } columnPos[i + 1] = columnPos[i] + columnWidth[i] + GapBetweenTableColumns; } float tableLeftX = columnPos[0]; float tableRightX = columnPos[tblColumnsCount] - GapBetweenTableColumns; /// Horizontal line float horY = top + 1.5f * spacingOne4Header; e.Graphics.DrawLine(new Pen(Color.Black, 2), new PointF(tableLeftX, horY), new PointF(tableRightX, horY)); /// /// Draw table header /// for (int i = 0; i < tblColumnsCount; i++) { PrintAt(e, columnPos[i], top + 1.7f * spacingOne4Header, columnWidth[i], header[i], headerFont, Alignment.Center); } /// Horizontal line horY = top + 2.1f * spacingOne4Header + headerHeight; e.Graphics.DrawLine(new Pen(Color.Black, 2), new PointF(tableLeftX, horY), new PointF(tableRightX, horY)); /// /// Draw table body /// int rowsCount = 0; foreach (var oneTableRow in rows) { for (int i = 0; i < tblColumnsCount; i++) { PrintAt(e, columnPos[i], horY + 0.2f * spacingOne4Header + spacingOne * rowsCount, columnWidth[i], oneTableRow[i], font, alignment[i]); } rowsCount++; } /// Horizontal line horY = horY + spacingOne * rowsCount + (3 * spacingOne4Header / 10); e.Graphics.DrawLine(new Pen(Color.Black, 2), new PointF(tableLeftX, horY), new PointF(tableRightX, horY)); return horY - top + spacingOne; } static Font lastFont = null; static float lastSpacing = 0; SizeF Measure(PrintPageEventArgs e, string paragraph, Font font) { if (font != lastFont) { lastFont = font; lastSpacing = e.Graphics.MeasureString("Abcgq", font).Height; } string[] lines = paragraph.Split(new char[] { '~' }); float width = 0; foreach (var line in lines) { float lineWid = e.Graphics.MeasureString(line, font).Width; if (lineWid > width) width = lineWid; } return new SizeF(width, 0.8f * lines.Length * lastSpacing + 0.2f); } void PrintAt(PrintPageEventArgs e, float x, float y, float width, string paragraph, Font font, Alignment alignment) { SizeF size = Measure(e, paragraph, font); string[] lines = paragraph.Split(new char[] { '~' }); foreach (var line in lines) { float xx; switch (alignment) { default: case Alignment.Left: xx = x; break; case Alignment.Center: xx = x + (width - e.Graphics.MeasureString(line, font).Width) / 2; break; case Alignment.Right: xx = x + width - e.Graphics.MeasureString(line, font).Width; break; } e.Graphics.DrawString(line, font, Brushes.Black, new PointF(xx, y)); y += 0.8f * lastSpacing; } } } }