using FastReport; using FastReport.DevComponents.DotNetBar; using FastReport.Utils; using InABox.Core; using InABox.Reports.CustomObjects; using netDxf; using Syncfusion.Windows.PdfViewer; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InABox.Reports { public class MultiSignatureObject : MultiItemObject { private FillBase textFill; private IDictionary? signatureData { get; set; } [Category("Data")] public string? DataColumn { get; set; } [Category("Data")] public Dictionary? Signatures { get; set; } [Category("Layout")] public float LabelHeight { get; set; } [Category("Appearance")] [Editor("FastReport.TypeEditors.FillEditor, FastReport", typeof(UITypeEditor))] public FillBase TextFill { get { return textFill; } set { if (value == null) throw new ArgumentNullException("TextFill"); textFill = value; if (!String.IsNullOrEmpty(Style)) Style = ""; } } [Category("Appearance")] public Font Font { get; set; } public MultiSignatureObject() { LabelHeight = 20; textFill = new SolidFill(Color.Black); Font = DrawUtils.DefaultReportFont; } public override void GetData() { base.GetData(); if (!string.IsNullOrEmpty(DataColumn)) { signatureData = null; Signatures = null; object data = Report.GetColumnValueNullable(DataColumn); if (data is IDictionary dict) { SetSignatureData(dict); } } } private void SetSignatureData(IDictionary signatureData) { this.signatureData = signatureData; } private class SignatureItem : Item { public string Name; public Image Image; public float Width => Image.Width; public float Height { get; } public SignatureItem(string name, Image image, float labelHeight) { Name = name; Image = image; Height = image.Height + labelHeight; } } #region Serialization public override void Serialize(FRWriter writer) { base.Serialize(writer); MultiSignatureObject c = writer.DiffObject as MultiSignatureObject; if (DataColumn != c.DataColumn) writer.WriteValue("DataColumn", DataColumn); if ((writer.SerializeTo != SerializeTo.Preview || Font != c.Font) && writer.ItemName != "inherited") writer.WriteValue("Font", Font); if(TextFill != c.TextFill) TextFill.Serialize(writer, "TextFill", c.TextFill); if (writer.SerializeTo != SerializeTo.SourcePages) { if (writer.SerializeTo == SerializeTo.Preview || String.IsNullOrEmpty(DataColumn)) { if (Signatures == null && signatureData != null) writer.WriteStr("Images", Serialization.Serialize(signatureData)); else if (!writer.AreEqual(Signatures, c.Signatures)) writer.WriteValue("Images", Signatures); } } } public override void Deserialize(FRReader reader) { base.Deserialize(reader); if (reader.HasProperty("TextFill")) { TextFill.Deserialize(reader, "TextFill"); } if (reader.HasProperty("Images")) { SetSignatureData(Serialization.Deserialize>(reader.ReadStr("Images"))); } switch (reader.DeserializeFrom) { case SerializeTo.Undo: case SerializeTo.Preview: case SerializeTo.Clipboard: // skip break; default: if (!reader.HasProperty("Font") && reader.ItemName != "inherited") { string creatorVersion = reader.Root.GetProp("ReportInfo.CreatorVersion"); if (!String.IsNullOrEmpty(creatorVersion)) { try { string[] versions = creatorVersion.Split('.'); int major = 0; if (Int32.TryParse(versions[0], out major)) { if (major < 2016) { Font = new Font("Arial", 10); } } } catch { } } } break; } } #endregion public override void Assign(Base source) { base.Assign(source); if (source is MultiSignatureObject src) { DataColumn = src.DataColumn; TextFill = src.TextFill; Font = src.Font; Signatures = src.Signatures == null ? null : src.Signatures.ToDictionary(x => x.Key, x => x.Value.Clone() as Image); if (src.Signatures == null && src.signatureData != null) { signatureData = src.signatureData; } } } public static Image? Load(byte[] bytes) { if (bytes != null && bytes.Length > 0) { try { #if CROSSPLATFORM // TODO memory leaks image converter return Image.FromStream(new MemoryStream(bytes)); #else return new ImageConverter().ConvertFrom(bytes) as Image; #endif } catch { Bitmap errorBmp = new Bitmap(10, 10); using (Graphics g = Graphics.FromImage(errorBmp)) { g.DrawLine(Pens.Red, 0, 0, 10, 10); g.DrawLine(Pens.Red, 0, 10, 10, 0); } return errorBmp; } } return null; } private void LoadSignatures() { if (signatureData == null) return; Signatures = new(); var oldData = new Dictionary(); foreach(var (name, data) in signatureData) { var saveImageData = data; // FImageData will be reset after this line, keep it var newImage = Load(data); if (newImage != null) { Signatures.Add(name, newImage); } oldData[name] = saveImageData; } signatureData = oldData; } internal StringFormat GetStringFormat(GraphicCache cache, StringFormatFlags flags, float scale) { StringAlignment align = StringAlignment.Near; if (HorizontalAlignment == System.Windows.HorizontalAlignment.Center) align = StringAlignment.Center; else if (HorizontalAlignment == System.Windows.HorizontalAlignment.Right) align = StringAlignment.Far; StringAlignment lineAlign = StringAlignment.Near; if (VerticalAlignment == System.Windows.VerticalAlignment.Center) lineAlign = StringAlignment.Center; else if (VerticalAlignment == System.Windows.VerticalAlignment.Bottom) lineAlign = StringAlignment.Far; return cache.GetStringFormat(align, lineAlign, StringTrimming.None, flags, 0f, 0f); } protected override void DrawItem(FRPaintEventArgs e, Item item, float x, float y, float w, float h) { if (item is SignatureItem signature) { var imageBoxWidth = w; var imageBoxHeight = h - LabelHeight; var aspectRatio = signature.Image.Width / signature.Image.Height; float imageWidth; float imageHeight; if (aspectRatio < imageBoxWidth / imageBoxHeight) { imageHeight = imageBoxHeight; imageWidth = imageHeight * aspectRatio; } else { imageWidth = imageBoxWidth; imageHeight = imageWidth / aspectRatio; } float imageX = x; float imageY = y; switch (HorizontalAlignment) { case System.Windows.HorizontalAlignment.Center: imageX += imageBoxWidth / 2 - imageWidth / 2; break; case System.Windows.HorizontalAlignment.Right: imageX += imageBoxWidth - imageWidth; break; case System.Windows.HorizontalAlignment.Stretch: imageWidth = imageBoxWidth; break; } switch (VerticalAlignment) { case System.Windows.VerticalAlignment.Center: imageY += imageBoxHeight / 2 - imageHeight / 2; break; case System.Windows.VerticalAlignment.Top: imageY += imageBoxHeight - imageHeight; break; case System.Windows.VerticalAlignment.Stretch: imageHeight = imageBoxHeight; break; } RectangleF textRect = new RectangleF( x * e.ScaleX, (y + h - LabelHeight) * e.ScaleY, w * e.ScaleX, h * e.ScaleY); Brush textBrush = null; if (TextFill is SolidFill) textBrush = e.Cache.GetBrush((TextFill as SolidFill).Color); else textBrush = TextFill.CreateBrush(textRect, e.ScaleX, e.ScaleY); Font font = e.Cache.GetFont(Font.FontFamily, IsPrinting ? Font.Size : Font.Size * e.ScaleX * 96f / DrawUtils.ScreenDpi, Font.Style); e.Graphics.DrawImage(signature.Image, imageX * e.ScaleX, imageY * e.ScaleY, imageWidth * e.ScaleX, imageHeight * e.ScaleY); var horz = HorizontalAlignment switch { System.Windows.HorizontalAlignment.Right => HorzAlign.Right, System.Windows.HorizontalAlignment.Center => HorzAlign.Center, System.Windows.HorizontalAlignment.Stretch => HorzAlign.Justify, _ => HorzAlign.Left }; var vert = VerticalAlignment switch { System.Windows.VerticalAlignment.Bottom => VertAlign.Bottom, System.Windows.VerticalAlignment.Center => VertAlign.Center, System.Windows.VerticalAlignment.Stretch => VertAlign.Top, _ => VertAlign.Top }; StringFormat format = GetStringFormat(e.Cache, 0, e.ScaleX); // use advanced rendering AdvancedTextRenderer advancedRenderer = new AdvancedTextRenderer(signature.Name, e.Graphics, font, textBrush, null, textRect, format, horz, vert, LabelHeight * e.ScaleY, 0, 1, false, false, false, false, e.ScaleX * 96f / DrawUtils.ScreenDpi, IsPrinting ? 1 : e.ScaleX * 96f / DrawUtils.ScreenDpi, null, IsPrinting); advancedRenderer.Draw(); } } protected override IList? LoadItems() { if (Signatures == null) { LoadSignatures(); } if (Signatures == null) { return null; } return Signatures.Select(x => new SignatureItem(x.Key, x.Value, LabelHeight) as Item).ToList(); } } }