namespace LehrerApp.Templating; public enum PlaceholderType { Text, Multiline, Date, Number, Image, Table, Chart } public abstract record PlaceholderValue(PlaceholderType Type); public sealed record TextValue(string Value) : PlaceholderValue(PlaceholderType.Text); public sealed record MultilineValue(string Value) : PlaceholderValue(PlaceholderType.Multiline); public sealed record DateValue(DateOnly Value) : PlaceholderValue(PlaceholderType.Date); public sealed record NumberValue(decimal Value) : PlaceholderValue(PlaceholderType.Number); public sealed record ImageValue(byte[] Data, string MimeType) : PlaceholderValue(PlaceholderType.Image); public sealed record ImageMetadata(int Width, int Height); public sealed record TableValue(IReadOnlyList Columns, IReadOnlyList> Rows) : PlaceholderValue(PlaceholderType.Table); public sealed record ChartPoint(string X, decimal Y); public sealed record ChartSeries(string Label, IReadOnlyList Points); public sealed record ChartValue(IReadOnlyList Series) : PlaceholderValue(PlaceholderType.Chart); public interface ITemplateDataProvider { IReadOnlyDictionary GetValues(); } public sealed record PageSizeDefinition(float Width, float Height, string Unit = "mm"); public sealed record PlaceholderDefinition(string Name, PlaceholderType Type, bool Required = false); public sealed class TemplateManifest { public int SchemaVersion { get; set; } = 1; public string Id { get; set; } = "neue-vorlage"; public string Name { get; set; } = "Neue Vorlage"; public string Description { get; set; } = ""; public PageSizeDefinition PageSize { get; set; } = new(210, 297); public string LayoutFile { get; set; } = "layout.tpl"; public List Placeholders { get; set; } = []; } public abstract record TemplateElement(int Line, float X, float Y, float Width, float Height, IReadOnlyDictionary Attributes); public sealed record BackgroundElement(int Line, string Path) : TemplateElement(Line, 0, 0, 0, 0, EmptyAttributes.Value); public sealed record ImageElement(int Line, string Path, float X, float Y, float Width, float Height, IReadOnlyDictionary Attributes) : TemplateElement(Line, X, Y, Width, Height, Attributes); public sealed record TextElement(int Line, float X, float Y, string Content, string? Placeholder, string? Format, IReadOnlyDictionary Attributes) : TemplateElement(Line, X, Y, 0, 0, Attributes); public sealed record TextBoxElement(int Line, float X, float Y, float Width, float Height, string Content, string? Placeholder, string? Format, IReadOnlyDictionary Attributes) : TemplateElement(Line, X, Y, Width, Height, Attributes); public sealed record TableElement(int Line, float X, float Y, float Width, float Height, string Placeholder, IReadOnlyDictionary Attributes) : TemplateElement(Line, X, Y, Width, Height, Attributes); public sealed record ChartElement(int Line, float X, float Y, float Width, float Height, string Placeholder, string ChartType, IReadOnlyDictionary Attributes) : TemplateElement(Line, X, Y, Width, Height, Attributes); internal static class EmptyAttributes { public static readonly IReadOnlyDictionary Value = new Dictionary(); } public sealed record TemplateLayout(float Width, float Height, string Unit, IReadOnlyList Elements); public sealed record LoadedTemplate(TemplateManifest Manifest, TemplateLayout Layout, IReadOnlyDictionary Assets, string SourceName = ""); public enum ValidationSeverity { Warning, Error } public sealed record ValidationIssue(ValidationSeverity Severity, string Message, int? Line = null); public sealed record ValidationResult(IReadOnlyList Issues) { public bool IsValid => Issues.All(x => x.Severity != ValidationSeverity.Error); public static ValidationResult Success { get; } = new([]); } public sealed class TemplateValidationException(ValidationResult result) : Exception(string.Join(Environment.NewLine, result.Issues.Select(x => x.Message))) { public ValidationResult Result { get; } = result; } public interface ITemplateRenderer { byte[] RenderToPdf(LoadedTemplate template, ITemplateDataProvider data); } public interface ITemplatePreviewRenderer { byte[] RenderFirstPageToPng(LoadedTemplate template, ITemplateDataProvider data, int dpi = 120); } public interface ITemplateLoader { LoadedTemplate LoadFromPackage(string packagePath); LoadedTemplate LoadFromPackage(Stream package, string sourceName = ""); ValidationResult Validate(LoadedTemplate template, IReadOnlyDictionary providedTypes); } public interface ITemplateMigrator { int SourceVersion { get; } TemplateManifest Migrate(TemplateManifest manifest); }