From 9cadc8e901fce6a516176a9c7611f48073730aea Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 30 Jan 2026 13:02:34 +0100 Subject: [PATCH] Add PSPDFKit annotation model and utilities Introduce classes and interfaces for modeling PDF annotations, including support for layout, relative positioning, background rendering, and color. Added Annotation, AnnotationParams, Background, Color, Extensions, and IAnnotation to EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation. Enables flexible annotation management and rendering. --- .../Models/PsPdfKitAnnotation/Annotation.cs | 92 +++++++++++++++++++ .../PsPdfKitAnnotation/AnnotationParams.cs | 79 ++++++++++++++++ .../Models/PsPdfKitAnnotation/Background.cs | 58 ++++++++++++ .../Models/PsPdfKitAnnotation/Color.cs | 10 ++ .../Models/PsPdfKitAnnotation/Extensions.cs | 8 ++ .../Models/PsPdfKitAnnotation/IAnnotation.cs | 22 +++++ 6 files changed, 269 insertions(+) create mode 100644 EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Annotation.cs create mode 100644 EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/AnnotationParams.cs create mode 100644 EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Background.cs create mode 100644 EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Color.cs create mode 100644 EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Extensions.cs create mode 100644 EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/IAnnotation.cs diff --git a/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Annotation.cs b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Annotation.cs new file mode 100644 index 00000000..e99deb58 --- /dev/null +++ b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Annotation.cs @@ -0,0 +1,92 @@ +using System.Text.Json.Serialization; + +namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation; + +public record Annotation : IAnnotation +{ + public required string Name { get; init; } + + #region Bound Annotation + [JsonIgnore] + public string? HorBoundAnnotName { get; init; } + + [JsonIgnore] + public string? VerBoundAnnotName { get; init; } + #endregion + + #region Layout + [JsonIgnore] + public double? MarginLeft { get; set; } + + [JsonIgnore] + public double MarginLeftRatio { get; init; } = 1; + + [JsonIgnore] + public double? MarginTop { get; set; } + + [JsonIgnore] + public double MarginTopRatio { get; init; } = 1; + + public double? Width { get; set; } + + [JsonIgnore] + public double WidthRatio { get; init; } = 1; + + public double? Height { get; set; } + + [JsonIgnore] + public double HeightRatio { get; init; } = 1; + #endregion + + #region Position + public double Left => (MarginLeft ?? 0) + (HorBoundAnnot?.HorBoundary ?? 0); + + public double Top => (MarginTop ?? 0) + (VerBoundAnnot?.VerBoundary ?? 0); + #endregion + + #region Boundary + [JsonIgnore] + public double HorBoundary => Left + (Width ?? 0); + + [JsonIgnore] + public double VerBoundary => Top + (Height ?? 0); + #endregion + + #region BoundAnnot + [JsonIgnore] + public Annotation? HorBoundAnnot { get; set; } + + [JsonIgnore] + public Annotation? VerBoundAnnot { get; set; } + #endregion + + public Color? BackgroundColor { get; init; } + + #region Border + public Color? BorderColor { get; init; } + + public string? BorderStyle { get; init; } + + public int? BorderWidth { get; set; } + #endregion + + [JsonIgnore] + internal Annotation Default + { + set + { + // To set null value, annotation must have null (0) value but null must has non-null value + if (MarginLeft == null && value.MarginLeft != null) + MarginLeft = value.MarginLeft * MarginLeftRatio; + + if (MarginTop == null && value.MarginTop != null) + MarginTop = value.MarginTop * MarginTopRatio; + + if (Width == null && value.Width != null) + Width = value.Width * WidthRatio; + + if (Height == null && value.Height != null) + Height = value.Height * HeightRatio; + } + } +}; \ No newline at end of file diff --git a/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/AnnotationParams.cs b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/AnnotationParams.cs new file mode 100644 index 00000000..6413a20d --- /dev/null +++ b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/AnnotationParams.cs @@ -0,0 +1,79 @@ +using System.Text.Json.Serialization; + +namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation; + +public class AnnotationParams +{ + public AnnotationParams() + { + _AnnotationJSObjectInitor = new(CreateAnnotationJSObject); + } + + public Background? Background { get; init; } + + #region Annotation + [JsonIgnore] + public Annotation? DefaultAnnotation { get; init; } + + private readonly List _annots = new List(); + + public bool TryGet(string name, out Annotation annotation) + { +#pragma warning disable CS8601 // Possible null reference assignment. + annotation = _annots.FirstOrDefault(a => a.Name == name); +#pragma warning restore CS8601 // Possible null reference assignment. + return annotation is not null; + } + + public required IEnumerable Annotations + { + get => _annots; + init + { + _annots = value.ToList(); + + if (DefaultAnnotation is not null) + foreach (var annot in _annots) + annot.Default = DefaultAnnotation; + + for (int i = 0; i < _annots.Count; i++) + { + #region set bound annotations + // horizontal + if (_annots[i].HorBoundAnnotName is string horBoundAnnotName) + if (TryGet(horBoundAnnotName, out var horBoundAnnot)) + _annots[i].HorBoundAnnot = horBoundAnnot; + else + throw new InvalidOperationException($"{horBoundAnnotName} added as bound anotation. However, it is not defined."); + + // vertical + if (_annots[i].VerBoundAnnotName is string verBoundAnnotName) + if (TryGet(verBoundAnnotName, out var verBoundAnnot)) + _annots[i].VerBoundAnnot = verBoundAnnot; + else + throw new InvalidOperationException($"{verBoundAnnotName} added as bound anotation. However, it is not defined."); + #endregion + } + } + } + #endregion + + #region AnnotationJSObject + private Dictionary CreateAnnotationJSObject() + { + var dict = _annots.ToDictionary(a => a.Name.ToLower(), a => a as IAnnotation); + + if (Background is not null) + { + Background.Locate(_annots); + dict.Add(Background.Name.ToLower(), Background); + } + + return dict; + } + + private readonly Lazy> _AnnotationJSObjectInitor; + + public Dictionary AnnotationJSObject => _AnnotationJSObjectInitor.Value; + #endregion +} diff --git a/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Background.cs b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Background.cs new file mode 100644 index 00000000..16d139e9 --- /dev/null +++ b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Background.cs @@ -0,0 +1,58 @@ +using System.Text.Json.Serialization; + +namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation; + +/// +/// The Background is an annotation for the PSPDF Kit. However, it has no function. +/// It is only the first annotation as a background for other annotations. +/// +public record Background : IAnnotation +{ + [JsonIgnore] + public double Margin { get; init; } + + public string Name { get; } = "Background"; + + public double? Width { get; set; } + + public double? Height { get; set; } + + public double Left { get; set; } + + public double Top { get; set; } + + public Color? BackgroundColor { get; init; } + + #region Border + public Color? BorderColor { get; init; } + + public string? BorderStyle { get; init; } + + public int? BorderWidth { get; set; } + #endregion + + public void Locate(IEnumerable annotations) + { + // set Top + if (annotations.MinBy(a => a.Top)?.Top is double minTop) + Top = minTop; + + // set Left + if (annotations.MinBy(a => a.Left)?.Left is double minLeft) + Left = minLeft; + + // set Width + if(annotations.MaxBy(a => a.GetRight())?.GetRight() is double maxRight) + Width = maxRight - Left; + + // set Height + if (annotations.MaxBy(a => a.GetBottom())?.GetBottom() is double maxBottom) + Height = maxBottom - Top; + + // add margins + Top -= Margin; + Left -= Margin; + Width += Margin * 2; + Height += Margin * 2; + } +} diff --git a/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Color.cs b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Color.cs new file mode 100644 index 00000000..6672edd1 --- /dev/null +++ b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Color.cs @@ -0,0 +1,10 @@ +namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation; + +public record Color +{ + public int R { get; init; } = 0; + + public int G { get; init; } = 0; + + public int B { get; init; } = 0; +} diff --git a/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Extensions.cs b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Extensions.cs new file mode 100644 index 00000000..35f0be06 --- /dev/null +++ b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/Extensions.cs @@ -0,0 +1,8 @@ +namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation; + +public static class Extensions +{ + public static double GetRight(this IAnnotation annotation) => annotation.Left + annotation?.Width ?? 0; + + public static double GetBottom(this IAnnotation annotation) => annotation.Top + annotation?.Height ?? 0; +} diff --git a/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/IAnnotation.cs b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/IAnnotation.cs new file mode 100644 index 00000000..d6ab1019 --- /dev/null +++ b/EnvelopeGenerator.GeneratorAPI/Models/PsPdfKitAnnotation/IAnnotation.cs @@ -0,0 +1,22 @@ +namespace EnvelopeGenerator.GeneratorAPI.Models.PsPdfKitAnnotation; + +public interface IAnnotation +{ + string Name { get; } + + double? Width { get; } + + double? Height { get; } + + double Left { get; } + + double Top { get; } + + Color? BackgroundColor { get; } + + Color? BorderColor { get; } + + string? BorderStyle { get; } + + int? BorderWidth { get; } +}