using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace FastReport.Utils { public struct ValidationError { public enum ErrorLevel { Warning, Error } public string Name; public ErrorLevel Level; public string Message; public ReportComponentBase Object; public ValidationError(string name, ErrorLevel level, string message, ReportComponentBase obj) { this.Name = name; this.Level = level; this.Message = message; this.Object = obj; } } /// /// Contains methods used for validation of report. /// public static class Validator { internal static void NormalizeBounds(ref RectangleF bounds) { if (bounds.Width < 0) { bounds.X = bounds.Right; bounds.Width = -bounds.Width; } if (bounds.Height < 0) { bounds.Y = bounds.Bottom; bounds.Height = -bounds.Height; } } internal static void GetIntersectingObjects(List list, BandBase band) { int n = band.Objects.Count; for (int i = 0; i < n; i++) { var bounds = band.Objects[i].Bounds; if (bounds.Width < 0 || bounds.Height < 0) NormalizeBounds(ref bounds); // compensate for inaccuracy of designer's grid fit bounds.Inflate(-0.01f, -0.01f); for (int j = 0; j < n; j++) { var bounds1 = band.Objects[j].Bounds; if (bounds1.Width < 0 || bounds1.Height < 0) NormalizeBounds(ref bounds1); if (i != j && bounds.IntersectsWith(bounds1)) { list.Add(band.Objects[i]); break; } } } } internal static bool RectContainInOtherRect(RectangleF parent, RectangleF child) { NormalizeBounds(ref parent); NormalizeBounds(ref child); // compensate for inaccuracy of designer's grid fit child.Inflate(-0.01f, -0.01f); return parent.Contains(child); } /// /// Validate report. /// /// /// Need set false if enabled backlight intersecting objects and report is designing. /// Token for cancelling method if it execute in thread. /// List of errors. public static List ValidateReport(Report report, bool checkIntersectObj = true, CancellationToken token = default) { if (report == null) return null; List listError = new List(); try { foreach (PageBase page in report.Pages) { foreach (Base c in page.AllObjects) { if (token.IsCancellationRequested) return null; if (c is BandBase band && checkIntersectObj) { List intersectingObjects = new List(); GetIntersectingObjects(intersectingObjects, band); foreach (var obj in intersectingObjects) { listError.Add(new ValidationError(obj.Name, ValidationError.ErrorLevel.Warning, Res.Get("Messages,Validator,IntersectedObjects"), obj)); } } if (c is ReportComponentBase comp) listError.AddRange(comp.Validate()); } } bool duplicateName; var objects = report.AllObjects; for (int i = 0; i < objects.Count - 1; i++) { duplicateName = false; for (int j = i + 1; j < objects.Count; j++) { if (token.IsCancellationRequested) return null; if (objects[j] is ReportComponentBase && objects[i].Name == objects[j].Name) { listError.Add(new ValidationError(objects[j].Name, ValidationError.ErrorLevel.Error, Res.Get("Messages,Validator,DuplicateName"), (ReportComponentBase)objects[j])); duplicateName = true; } } if (objects[i] is ReportComponentBase && duplicateName) listError.Add(new ValidationError(objects[i].Name, ValidationError.ErrorLevel.Error, Res.Get("Messages,Validator,DuplicateName"), (ReportComponentBase)objects[i])); } } catch { // validator should not crash the app return null; } return listError.Distinct().ToList(); } } }