| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 | using System;using System.Collections.Generic;using System.Drawing;using System.Linq;using System.Text.RegularExpressions;using System.Windows.Forms;using FastReport;using FastReport.Table;using FastReport.Utils;using InABox.Core;using InABox.Scripting;using InABox.Wpf.Reports;using InABox.Wpf.Reports.CustomObjects;using UnderlineType = InABox.Core.UnderlineType;namespace InABox.DynamicGrid{    public static class DigitalFormUtils    {                #region Layout Importer                        private class Cell        {            public string Content { get; set; }            public int Row { get; set; }            public int Column { get; set; }            public int RowSpan { get; set; } = 1;            public int ColumnSpan { get; set; } = 1;            public ICell InnerCell { get; set; }            public Cell(int row, int column, string content, ICell cell)            {                Row = row;                Column = column;                Content = content;                InnerCell = cell;            }        }        private static void DeleteColumn(List<Cell> cells, int column)        {            foreach(var cell in cells)            {                if(cell.Column <= column && cell.Column + cell.ColumnSpan - 1 >= column)                {                    --cell.ColumnSpan;                }                else if(cell.Column > column)                {                    --cell.Column;                }            }            cells.RemoveAll(x => x.ColumnSpan < 0);        }        private static List<Cell> GetCells(ISheet sheet)        {            var grid = new Dictionary<int, Dictionary<int, Cell>>();            for (int rowIdx = sheet.FirstRow; rowIdx <= sheet.LastRow; ++rowIdx)            {                var row = sheet.GetRow(rowIdx);                if (row is not null && row.FirstColumn >= 0)                {                    var rowCells = new Dictionary<int, Cell>();                    for (int colIdx = row.FirstColumn; colIdx <= row.LastColumn; ++colIdx)                    {                        var cell = row.GetCell(colIdx);                        if (cell is not null)                        {                            rowCells.Add(colIdx, new Cell(rowIdx, colIdx, cell.GetValue(), cell));                        }                    }                    grid.Add(rowIdx, rowCells);                }            }            foreach (var region in sheet.GetMergedCells())            {                for (int r = region.FirstRow; r <= region.LastRow; ++r)                {                    if (!grid.TryGetValue(r, out var row)) continue;                    for (int c = region.FirstColumn; c <= region.LastColumn; ++c)                    {                        if ((r - region.FirstRow) + (c - region.FirstColumn) != 0)                        {                            row.Remove(c);                        }                    }                    if (row.Count == 0)                    {                        grid.Remove(r);                    }                }                if (grid.TryGetValue(region.FirstRow, out var cRow) && cRow.TryGetValue(region.FirstColumn, out var cCell))                {                    cCell.RowSpan = region.LastRow - region.FirstRow + 1;                    cCell.ColumnSpan = region.LastColumn - region.FirstColumn + 1;                }            }            var cells = new List<Cell>();            foreach (var row in grid.Values)            {                foreach (var cell in row.Values)                {                    cells.Add(cell);                }            }            return cells;        }        private static Regex VariableRegex = new(@"^\[(?<VAR>[^:\]]+)(?::(?<TYPE>[^:\]]*))?(?::(?<PROPERTIES>[^\]]*))?\]$");        private static Regex HeaderRegex = new(@"^{(?<HEADER>[^:}]+)(?::(?<COLLAPSED>[^}]*))?}$");        public static DFLayout LoadLayout(ISpreadsheet spreadsheet)        {            var sheet = spreadsheet.GetSheet(0);            var cells = GetCells(sheet);            int firstRow = int.MaxValue;            int lastRow = 0;            int firstCol = int.MaxValue;            int lastCol = 0;            foreach (var cell in cells)            {                firstCol = Math.Min(cell.Column, firstCol);                lastCol = Math.Max(cell.Column + cell.ColumnSpan - 1, lastCol);                firstRow = Math.Min(cell.Row, firstRow);                lastRow = Math.Max(cell.Row + cell.RowSpan - 1, lastRow);            }            var layout = new DFLayout();            var columnWidths = new Dictionary<int, float>();            var colOffset = 0;            for (int col = firstCol; col <= lastCol; ++col)            {                var width = sheet.GetColumnWidth(col);                if(width == float.MinValue)                {                    layout.ColumnWidths.Add("10*");                }                else if(width <= 0f)                {                    DeleteColumn(cells, col);                }                else                {                    layout.ColumnWidths.Add($"{width}*");                }            }            for (int row = firstRow; row <= lastRow; ++row)                layout.RowHeights.Add("Auto");                foreach(var cell in cells)            {                var style = cell.InnerCell.GetStyle();                if (string.IsNullOrWhiteSpace(cell.Content) && style.Foreground == Color.Empty) continue;                DFLayoutControl? control;                var content = cell.Content?.Trim() ?? "";                var headermatch = HeaderRegex.Match(content);                var variablematch = VariableRegex.Match(content);                if (headermatch.Success)                {                    var text = headermatch.Groups["HEADER"];                    var collapsed = headermatch.Groups["COLLAPSED"];                    var header = new DFLayoutHeader()                    {                        Header = text.Value,                         Collapsed = collapsed.Success && String.Equals(collapsed.Value.ToUpper(),"COLLAPSED"),                    };                    header.Style.Synchronise(CreateStyle(style));                                        control = header;                }                else if (variablematch.Success)                {                    var variableName = variablematch.Groups["VAR"];                    var variableType = variablematch.Groups["TYPE"];                    var variableProps = variablematch.Groups["PROPERTIES"];                                        Type? fieldType = null;                    if (variableType.Success)                        fieldType = DFUtils.GetFieldType(variableType.Value);                    fieldType ??= typeof(DFLayoutStringField);                                        var field = (Activator.CreateInstance(fieldType) as DFLayoutField)!;                    field.Name = variableName.Value;                                        if (variableProps.Success)                    {                        if (field is DFLayoutOptionField option)                            option.Properties.Options = variableProps.Value;                        if (field is DFLayoutStringField text)                            text.Properties.TextWrapping = style.WrapText;                        // need to populate other variable types here                    }                                                            control = field;                }                else                {                    var label = new DFLayoutLabel                    {                        Caption = content                    };                    label.Style.Synchronise(CreateStyle(style));                    control = label;                }                                if(control is not null)                {                    control.Row = cell.Row - firstRow + 1;                    control.Column = cell.Column - firstCol + 1 - colOffset;                    control.RowSpan = cell.RowSpan;                    control.ColumnSpan = cell.ColumnSpan;                    layout.Elements.Add(control);                }            }            return layout;        }        private static DFLayoutTextStyle CreateStyle(ICellStyle style)        {            if (style == null)                return new DFLayoutTextStyle();            var result = new DFLayoutTextStyle            {                FontSize = style.Font.FontSize,                IsItalic = style.Font.Italic,                IsBold = style.Font.Bold,                Underline = style.Font.Underline switch                {                    Scripting.UnderlineType.None => UnderlineType.None,                    Scripting.UnderlineType.Single or Scripting.UnderlineType.SingleAccounting => UnderlineType.Single,                    Scripting.UnderlineType.Double or Scripting.UnderlineType.DoubleAccounting => UnderlineType.Double,                    _ => UnderlineType.None                },                BackgroundColour = style.Background,                ForegroundColour = style.Font.Colour,                HorizontalTextAlignment = style.HorizontalAlignment switch                {                     CellAlignment.Middle => DFLayoutAlignment.Middle,                     CellAlignment.End => DFLayoutAlignment.End,                     CellAlignment.Justify => DFLayoutAlignment.Stretch,                     _ => DFLayoutAlignment.Start                },                                VerticalTextAlignment = style.VerticalAlignment switch                {                    CellAlignment.Start => DFLayoutAlignment.Start,                    CellAlignment.End => DFLayoutAlignment.End,                    CellAlignment.Justify => DFLayoutAlignment.Stretch,                    _ => DFLayoutAlignment.Middle                },                                TextWrapping = style.WrapText            };            return result;        }                #endregion                #region Report Generator        private static string ElementName(HashSet<string> names, string name)        {            int i = 0;            if(names.Contains(name))            {                string newName;                do                {                    newName = $"{name}{i}";                    ++i;                } while (names.Contains(newName));                name = newName;            }            return name;        }                                public static Report? GenerateReport(DigitalFormLayout layout, DataModel model)        {                        bool IsValidChar(char c) =>  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789".Contains(c);                        var report = ReportUtils.SetupReport(null, model, true);            var dfLayout = new DFLayout();            dfLayout.LoadLayout(layout.Layout);            var page = new ReportPage();            page.Name = "Page1";            page.PaperWidth = 210;            page.PaperHeight = 297;            page.Landscape = false;            page.LeftMargin = 10;            page.TopMargin = 10;            page.RightMargin = 10;            page.BottomMargin = 10;            report.Pages.Add(page);            var formData = report.GetDataSource("Form_Data");            var band = new DataBand            {                Name = "Data1",                Height = Units.Millimeters * (page.PaperHeight - (page.TopMargin + page.BottomMargin)),                Width = Units.Millimeters * (page.PaperWidth - (page.LeftMargin + page.RightMargin)),                PrintIfDatasourceEmpty = true,                DataSource = formData,                StartNewPage = true            };            page.AddChild(band);            var logo = new PictureObject()            {                Height = 20F * Units.Millimeters,                Width = 30F * Units.Millimeters,                DataColumn = "CompanyLogo.Data"            };            band.AddChild(logo);            var company = new TextObject()            {                Left = band.Width - (90F * Units.Millimeters),                Width = 90F * Units.Millimeters,                Height = 5F * Units.Millimeters,                Text = "[CompanyInformation.CompanyName]",                HorzAlign = HorzAlign.Right,                VertAlign = VertAlign.Center,                Font = new System.Drawing.Font("Arial", 12F, FontStyle.Bold)            };            band.AddChild(company);            var address = new TextObject()            {                Left = band.Width - (90F * Units.Millimeters),                Width = 90F * Units.Millimeters,                Height = 15F * Units.Millimeters,                Top = 5F * Units.Millimeters,                Text = "[CompanyInformation.PostalAddress_Street]\n[CompanyInformation.PostalAddress_City] [CompanyInformation.PostalAddress_PostCode]",                HorzAlign = HorzAlign.Right,                VertAlign = VertAlign.Top,                Font = new System.Drawing.Font("Arial", 12F)            };            band.AddChild(address);                        var instancetable = model                .GetType()                .GetInterfaces()                .FirstOrDefault(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IDataModel<>))?                .GetGenericArguments()                .FirstOrDefault()?                .EntityName()                .Split('.')                .Last();            var title = new TextObject()            {                Top = 23F * Units.Millimeters,                Width = band.Width,                Height = 6F * Units.Millimeters,                Text = $"[Form_Data.{instancetable}.Number] - {layout.Form.Description}",                HorzAlign = HorzAlign.Center,                VertAlign = VertAlign.Center,                Font = new System.Drawing.Font("Arial", 14F, FontStyle.Bold)            };            band.AddChild(title);            /*            <TextObject Name="Text2" Left="500.85" Top="28.35" Width="207.9" Height="56.7" Text="16 Madrid Place
Maddington WA 6109
Phone: (08) 9492 1200
Email:  admin@com-al.com.au" HorzAlign="Right" Font="Arial, 9pt"/>            <PictureObject Name="Picture1" Left="9.45" Top="9.45" Width="113.4" Height="75.6" DataColumn="CompanyLogo.Data"/>            <TextObject Name="Text1" Left="444.15" Top="9.45" Width="264.6" Height="18.9" Text="Com-Al Windows Pty Ltd" HorzAlign="Right" Font="Arial, 10pt, style=Bold" TextFill.Color="RoyalBlue"/>            <TextObject Name="Text3" Left="9.45" Top="103.95" Width="699.3" Height="28.35" Text="TEST & TAG REPORT: [KanbanForm.Number]" HorzAlign="Center" VertAlign="Center" Font="Arial, 14pt, style=Bold"/>            */                        var elementNames = new HashSet<string>();            var table = new TableObject()            {                Name = "FormTable",                ColumnCount = dfLayout.ColumnWidths.Count,                RowCount = dfLayout.RowHeights.Count,                Top = 32F * Units.Millimeters            };            band.AddChild(table);            foreach(var element in dfLayout.Elements)            {                if (element.Row < 1 || element.Row + element.RowSpan - 1 > table.RowCount                    || element.Column < 1 || element.Column + element.ColumnSpan - 1 > table.ColumnCount) continue;                var row = table.Rows[element.Row - 1];                if(row.ChildObjects[element.Column - 1] is TableCell cell)                {                    cell.Border.Lines = BorderLines.All;                    cell.ColSpan = element.ColumnSpan;                    cell.RowSpan = element.RowSpan;                    if (element is DFLayoutField field)                    {                        var manualHeight = 0.0f;                        cell.Name = ElementName(elementNames, $"Cell_{new string(field.Name.Where(c => IsValidChar(c)).ToArray())}");                        var dataColumn = $"Form_Data.{field.Name}";                        if(field is DFLayoutEmbeddedImage || field is DFLayoutSignaturePad)                        {                            var picture = new PictureObject                            {                                DataColumn = dataColumn,                                //Dock = DockStyle.Fill,                                //Padding = new Padding(5,5,5,5)                            };                            //cell.Padding = new Padding(5);                            cell.AddChild(picture);                            manualHeight = 40;                        }                        else if(field is DFLayoutMultiImage)                        {                            var image = new MultiImageObject                            {                                DataColumn = dataColumn,                                //Dock = DockStyle.Fill,                                //Padding = new Padding(5,5,5,5)                            };                            cell.AddChild(image);                            manualHeight = 40;                        }                        else if(field is DFLayoutMultiSignaturePad)                        {                            var image = new MultiSignatureObject                            {                                DataColumn = dataColumn,                                //Dock = DockStyle.Fill,                                //Padding = new Padding(5,5,5,5)                            };                            cell.AddChild(image);                            manualHeight = 40;                        }                        else                        {                            cell.Text = $"[{dataColumn}]";                            cell.Font = new System.Drawing.Font(cell.Font.FontFamily, 10F, FontStyle.Italic);                            cell.TextColor = Color.Navy;                            cell.HorzAlign = HorzAlign.Left;                            cell.VertAlign = VertAlign.Center;                            if (field is DFLayoutStringField lsf)                                cell.WordWrap = lsf.Properties.TextWrapping;                        }                        if(manualHeight > 0 && dfLayout.RowHeights[element.Row - 1] == "Auto")                        {                            dfLayout.RowHeights[element.Row - 1] = manualHeight.ToString();                        }                    }                    else if (element is DFLayoutLabel label)                    {                        var background = label.Style.BackgroundColour;                        if (background == Color.Empty)                        {                            label.Style.BackgroundColour = Color.WhiteSmoke;                        }                        cell.Text = label.Description;                        cell.Name = ElementName(elementNames, "Label_" + element.Row + "_" + element.Column);                        ApplyStyle(cell, label.Style);                    }                    else if (element is DFLayoutHeader header)                    {                        cell.Name = ElementName(elementNames, "Header_" + element.Row + "_" + element.Column);                        cell.Text = header.Header;                        ApplyStyle(cell, header.Style);                    }                }            }            ProcessColumnWidths(band.Width, dfLayout.ColumnWidths, table.Columns);            ProcessRowHeights(band.Height, dfLayout.RowHeights, table.Rows);            return report;        }        private static void ApplyStyle(TableCell cell, DFLayoutTextStyle style)        {            var background = style.BackgroundColour;            if(background != Color.Empty) cell.FillColor = background;            var foreground = style.ForegroundColour;            if (foreground != Color.Empty) cell.TextColor = foreground;            FontStyle fontstyle = System.Drawing.FontStyle.Regular;            if (style.IsBold)                fontstyle |= System.Drawing.FontStyle.Bold;            if (style.IsItalic)                fontstyle |= System.Drawing.FontStyle.Italic;            if (style.Underline != UnderlineType.None)                fontstyle |= FontStyle.Underline;            float fontsize = (float)style.FontSize;            fontsize = fontsize == 0F ? 10F : fontsize;            cell.Font = new System.Drawing.Font(cell.Font.FontFamily, fontsize, fontstyle);            cell.HorzAlign = style.HorizontalTextAlignment switch            {                DFLayoutAlignment.Start => HorzAlign.Left,                DFLayoutAlignment.Middle => HorzAlign.Center,                DFLayoutAlignment.End => HorzAlign.Right,                DFLayoutAlignment.Stretch => HorzAlign.Justify,                _ => HorzAlign.Left            };            cell.VertAlign = style.VerticalTextAlignment switch            {                DFLayoutAlignment.Start => VertAlign.Top,                DFLayoutAlignment.Middle => VertAlign.Center,                DFLayoutAlignment.End => VertAlign.Bottom,                DFLayoutAlignment.Stretch => VertAlign.Center,                _ => VertAlign.Center            };            cell.WordWrap = style.TextWrapping;        }        private static void ProcessRowHeights(float bandheight, List<string> values, TableRowCollection rows)        {            float fixedtotal = 0F;            for (int iFixed = 0; iFixed < values.Count; iFixed++)            {                if (!values[iFixed].Contains("*"))                {                    if (!float.TryParse(values[iFixed], out float value))                        value = 25F / Units.Millimeters;                    else                        value = value / (1.5f * Units.Millimeters);                    rows[iFixed].Height = Units.Millimeters * value;                    fixedtotal += Units.Millimeters * value;                }            }            Dictionary<int, float> starvalues = new Dictionary<int, float>();            float startotal = 0F;            for (int iStar = 0; iStar < values.Count; iStar++)            {                if (values[iStar].Contains('*'))                {                    if (!float.TryParse(values[iStar].Replace("*", ""), out float value))                        value += 1;                    starvalues[iStar] = value;                    startotal += value;                }            }            foreach (var key in starvalues.Keys)                rows[key].Height = (starvalues[key] / startotal) * (bandheight - fixedtotal);                }        private static void ProcessColumnWidths(float bandwidth, List<string> values, TableColumnCollection columns)        {            float fixedtotal = 0F;            for (int iFixed = 0; iFixed < values.Count; iFixed++)            {                if (!values[iFixed].Contains("*"))                {                    if (!float.TryParse(values[iFixed], out float value))                        value = 40F;                    columns[iFixed].Width = Units.Millimeters * value;                    fixedtotal += Units.Millimeters * value;                }            }            Dictionary<int, float> starvalues = new Dictionary<int, float>();            float startotal = 0F;            for (int iStar = 0; iStar < values.Count; iStar++)            {                if (values[iStar].Contains('*'))                {                    if (!float.TryParse(values[iStar].Replace("*", ""), out float value))                        value += 1;                    starvalues[iStar] = value;                    startotal += value;                }            }                        foreach (var key in starvalues.Keys)                columns[key].Width = (starvalues[key] / startotal) * (bandwidth - fixedtotal);        }                #endregion                #region Data Model                private static List<Type>? _entityForms;        public static DataModel? GetDataModel(String appliesto, IEnumerable<DigitalFormVariable> variables)        {            _entityForms ??= CoreUtils.Entities                .Where(x => x.IsSubclassOfRawGeneric(typeof(EntityForm<,,>)))                .ToList();            var entityForm = _entityForms                .FirstOrDefault(x => x.GetSuperclassDefinition(typeof(EntityForm<,,>))                    ?.GenericTypeArguments[0].Name == appliesto);            if(entityForm is not null)            {                var model = (Activator.CreateInstance(typeof(DigitalFormReportDataModel<>).MakeGenericType(entityForm), Filter.Create(entityForm).None(), null) as DataModel)!;                (model as IDigitalFormReportDataModel)!.Variables = variables.ToArray();                return model;            }            return null;        }                #endregion            }    }
 |