Compare commits

..

13 Commits

Author SHA1 Message Date
Developer 02
7c57b7e332 refactor(network): add bias to annot.top 2025-04-24 02:23:22 +02:00
Developer 02
4bf91df85f fx(network): add bias to left position 2025-04-24 02:11:12 +02:00
Developer 02
50796b22d9 refactor(network): convert getAnnotation params to async method 2025-04-24 02:10:18 +02:00
Developer 02
2974ddb985 feat(ConfigController): add authorize attribute 2025-04-24 02:01:09 +02:00
Developer 02
54f39103e1 feat(IAnnotation): Hinzufügen und Implementieren der Eigenschaften BackgroundColor, BorderColor, BorderStyle und BorderWidth.
- Hintergrund mit neuen Eigenschaften in Annotations.js binden
2025-04-24 00:36:24 +02:00
Developer 02
8b505ae39a feat(annotations): verbessertes Styling für Hintergrund-Anmerkungen
- Hintergrundfarbe der Hintergrund-Anmerkungen auf einen benutzerdefinierten hellen Ton geändert
- Unterstrichener Rahmenstil mit spezifischer Farbe und Breite hinzugefügt
- Blendmodus von 'multiply' zu 'normal' vereinheitlicht für bessere Konsistenz
- Visuelle Hierarchie der Anmerkungselemente verbessert
2025-04-23 22:59:45 +02:00
Developer 02
d75da655d2 feat(Hintergrund): Locate add, um die Position des Hintergrunds anhand anderer Anmerkungen zu berechnen.
- Standort des Hintergrunds zu Anmerkungen hinzugefügt
2025-04-23 18:13:46 +02:00
Developer 02
e72ea534e5 refactor(AnnotationParams): AnnotationJSObject aktualisieren, um IAnnotation (anstelle von Annotation) zu enthalten
- Hintergrund in AnnotationJSObject hinzufügen, wenn er nicht null ist
2025-04-23 17:25:58 +02:00
Developer 02
e95d1d782e feat(IAnnotation): Erstellt, um die grundlegenden Eigenschaften einer Annotation zu behandeln.
- implementiert in Annotation und Hintergrund
2025-04-23 16:50:10 +02:00
Developer 02
32be5077f9 feat(Hintergrund): Erstellen, um den Hintergrund von Anmerkungen mit der Eigenschaft MarginRatio zu konfigurieren.
- verschiebt Anmerkungen nach
2025-04-23 15:41:53 +02:00
Developer 02
d80fa0b023 feat(Background): Add Width, Height, Top and Left properties.
- Add JsonIgnroe property to BackGround
2025-04-23 15:38:14 +02:00
Developer 02
ea6ee11a4e feat(Hintergrund): Erstellen, um den Hintergrund von Anmerkungen mit der Eigenschaft MarginRatio zu konfigurieren.
- verschiebt Anmerkungen nach
2025-04-23 15:31:35 +02:00
Developer 02
13a87f29d9 refactor(Annotations.js): Hinzufügen von Container-Widget-Annotationen als Hintergrund von Annotationen ohne BoundingBox-Konfiguration.
- Konvertieren von Signaturen Hintergrundfarbe von gelb zu hellgelb.
2025-04-23 13:38:32 +02:00
12 changed files with 230 additions and 52 deletions

View File

@@ -1,4 +1,5 @@
using EnvelopeGenerator.Web.Models;
using EnvelopeGenerator.Web.Models.Annotation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
@@ -6,6 +7,7 @@ namespace EnvelopeGenerator.Web.Controllers;
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ConfigController : ControllerBase
{
private readonly AnnotationParams _annotParams;
@@ -18,6 +20,6 @@ public class ConfigController : ControllerBase
[HttpGet("Annotations")]
public IActionResult GetAnnotationParams()
{
return Ok(_annotParams.AnnotationDictionary);
return Ok(_annotParams.AnnotationJSObject);
}
}

View File

@@ -1,8 +1,8 @@
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Web.Models;
namespace EnvelopeGenerator.Web.Models.Annotation;
public record Annotation
public record Annotation : IAnnotation
{
public required string Name { get; init; }
@@ -60,6 +60,16 @@ public record Annotation
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
{

View File

@@ -1,15 +1,21 @@
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Web.Models;
namespace EnvelopeGenerator.Web.Models.Annotation;
public class AnnotationParams
{
public AnnotationParams()
{
_AnnotationJSObjectInitor = new(CreateAnnotationJSObject);
}
public Background? Background { get; init; }
#region Annotation
[JsonIgnore]
public Annotation? DefaultAnnotation { get; init; }
private readonly IEnumerable<Annotation> _annots = new List<Annotation>();
public Annotation this[string name] => _annots.First(a => a.Name == name);
private readonly List<Annotation> _annots = new List<Annotation>();
public bool TryGet(string name, out Annotation annotation)
{
@@ -24,34 +30,50 @@ public class AnnotationParams
get => _annots;
init
{
_annots = value;
_annots = value.ToList();
if (DefaultAnnotation is not null)
foreach (var annot in _annots)
annot.Default = DefaultAnnotation;
foreach (var annot in _annots)
for (int i = 0; i < _annots.Count; i++)
{
#region set bound annotations
// horizontal
if (annot.HorBoundAnnotName is string horBoundAnnotName)
if (_annots[i].HorBoundAnnotName is string horBoundAnnotName)
if (TryGet(horBoundAnnotName, out var horBoundAnnot))
annot.HorBoundAnnot = horBoundAnnot;
_annots[i].HorBoundAnnot = horBoundAnnot;
else
throw new InvalidOperationException($"{horBoundAnnotName} added as bound anotation. However, it is not defined.");
// vertical
if (annot.VerBoundAnnotName is string verBoundAnnotName)
if (_annots[i].VerBoundAnnotName is string verBoundAnnotName)
if (TryGet(verBoundAnnotName, out var verBoundAnnot))
annot.VerBoundAnnot = verBoundAnnot;
_annots[i].VerBoundAnnot = verBoundAnnot;
else
throw new InvalidOperationException($"{verBoundAnnotName} added as bound anotation. However, it is not defined.");
#endregion
}
AnnotationDictionary = _annots.ToDictionary(a => a.Name.ToLower(), a => a);
}
}
#endregion
public Dictionary<string, Annotation> AnnotationDictionary { get; private init; } = new();
#region AnnotationJSObject
private Dictionary<string, IAnnotation> 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<Dictionary<string, IAnnotation>> _AnnotationJSObjectInitor;
public Dictionary<string, IAnnotation> AnnotationJSObject => _AnnotationJSObjectInitor.Value;
#endregion
}

View File

@@ -0,0 +1,58 @@
using System.Text.Json.Serialization;
namespace EnvelopeGenerator.Web.Models.Annotation;
/// <summary>
/// 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.
/// </summary>
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<IAnnotation> 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;
}
}

View File

@@ -0,0 +1,10 @@
namespace EnvelopeGenerator.Web.Models.Annotation;
public record Color
{
public int R { get; init; } = 0;
public int G { get; init; } = 0;
public int B { get; init; } = 0;
}

View File

@@ -0,0 +1,8 @@
namespace EnvelopeGenerator.Web.Models.Annotation;
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;
}

View File

@@ -0,0 +1,22 @@
namespace EnvelopeGenerator.Web.Models.Annotation;
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; }
}

View File

@@ -15,6 +15,7 @@ using DigitalData.EmailProfilerDispatcher;
using EnvelopeGenerator.Infrastructure;
using EnvelopeGenerator.Web.Sanitizers;
using EnvelopeGenerator.Application.Contracts.Services;
using EnvelopeGenerator.Web.Models.Annotation;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("Logging initialized!");

View File

@@ -151,6 +151,21 @@
},
"MainPageTitle": null,
"AnnotationParams": {
"Background": {
"Margin": 0.20,
"BackgroundColor": {
"R": 222,
"G": 220,
"B": 215
},
"BorderColor": {
"R": 204,
"G": 202,
"B": 198
},
"BorderStyle": "underline",
"BorderWidth": 4
},
"DefaultAnnotation": {
"Width": 1,
"Height": 0.5,

View File

@@ -5,6 +5,34 @@ async function createAnnotations(document, instance) {
for(var element of document.elements) {
const annotParams = await getAnnotationParams(element.left, element.top);
const page = element.page - 1
//background
if(annotParams.background){
let background = annotParams.background;
const id_background = PSPDFKit.generateInstantId();
const annotation_background = new PSPDFKit.Annotations.WidgetAnnotation({
id: id_background,
pageIndex: page,
formFieldName: id_background,
backgroundColor: background?.backgroundColor ? new PSPDFKit.Color(background.backgroundColor) : null,
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(background),
fontSize: 8,
borderStyle: background.borderStyle,
borderWidth: background.borderWidth,
borderColor: background?.borderColor ? new PSPDFKit.Color(background.borderColor) : null
});
const formFieldBackground = new PSPDFKit.FormFields.ButtonFormField({
name: id_background,
annotationIds: PSPDFKit.Immutable.List([annotation_background.id]),
value: "",
readOnly: false
});
signatures.push(annotation_background)
signatures.push(formFieldBackground)
}
//signatures
const id = PSPDFKit.generateInstantId()
@@ -12,8 +40,8 @@ async function createAnnotations(document, instance) {
id: id,
pageIndex: page,
formFieldName: id,
backgroundColor: PSPDFKit.Color.YELLOW,
blendMode: 'multiply',
backgroundColor: PSPDFKit.Color.LIGHT_YELLOW,
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(annotParams.signature),
})
@@ -29,7 +57,7 @@ async function createAnnotations(document, instance) {
pageIndex: page,
formFieldName: id_position,
backgroundColor: PSPDFKit.Color.DarkBlue,
blendMode: 'multiply',
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(annotParams.position),
fontSize: 8
})
@@ -48,7 +76,7 @@ async function createAnnotations(document, instance) {
pageIndex: page,
formFieldName: id_city,
backgroundColor: PSPDFKit.Color.DarkBlue,
blendMode: 'multiply',
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(annotParams.city),
fontSize: 8
})
@@ -67,7 +95,7 @@ async function createAnnotations(document, instance) {
pageIndex: page,
formFieldName: id_date,
backgroundColor: PSPDFKit.Color.DarkBlue,
blendMode: 'multiply',
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(annotParams.date),
fontSize: 8,
backgroundColor: PSPDFKit.Color.TRANSPARENT,
@@ -97,7 +125,7 @@ async function createAnnotations(document, instance) {
id: id_date_label,
pageIndex: page,
formFieldName: id_date_label,
blendMode: 'multiply',
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(annotParams.datelabel),
fontSize: 8,
backgroundColor: PSPDFKit.Color.TRANSPARENT,
@@ -119,7 +147,7 @@ async function createAnnotations(document, instance) {
id: id_city_label,
pageIndex: page,
formFieldName: id_city_label,
blendMode: 'multiply',
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(annotParams.citylabel),
fontSize: 8,
backgroundColor: PSPDFKit.Color.TRANSPARENT,
@@ -131,7 +159,8 @@ async function createAnnotations(document, instance) {
name: id_city_label,
annotationIds: PSPDFKit.Immutable.List([annotation_city_label.id]),
value: "Ort",
readOnly: true
readOnly: true,
color: PSPDFKit.Color.BLACK
})
//position label
@@ -140,7 +169,7 @@ async function createAnnotations(document, instance) {
id: id_position_label,
pageIndex: page,
formFieldName: id_position_label,
blendMode: 'multiply',
blendMode: 'normal',
boundingBox: new PSPDFKit.Geometry.Rect(annotParams.positionlabel),
fontSize: 8,
backgroundColor: PSPDFKit.Color.TRANSPARENT,

View File

@@ -175,21 +175,21 @@ async function setLanguage(language) {
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(langs => langs.includes(language))
.catch(err => false);
.then(res => res.json())
.then(langs => langs.includes(language))
.catch(err => false);
if(hasLang)
if (hasLang)
return await fetch(`/lang/${language}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
.then(response => {
if (response.redirected)
window.location.href = response.url;
else if (!response.ok)
return Promise.reject('Failed to set language');
});
.then(response => {
if (response.redirected)
window.location.href = response.url;
else if (!response.ok)
return Promise.reject('Failed to set language');
});
}
async function logout() {
@@ -204,22 +204,23 @@ async function logout() {
});
}
function getAnnotationParams(leftInInch = 0, topInInch = 0, inchToPointFactor = 72) {
return fetch(`${window.location.origin}/api/Config/Annotations`, {
async function getAnnotationParams(leftInInch = 0, topInInch = 0, inchToPointFactor = 72) {
const annotParams = await fetch(`${window.location.origin}/api/Config/Annotations`, {
credentials: 'include',
method: 'GET'
})
.then(res => res.json())
.then(annotParams => {
for(var key in annotParams){
var annot = annotParams[key];
annot.width *= inchToPointFactor;
annot.height *= inchToPointFactor;
annot.left += leftInInch;
annot.left *= inchToPointFactor;
annot.top += topInInch;
annot.top *= inchToPointFactor;
}
return annotParams;
});
.then(res => res.json());
for (var key in annotParams) {
var annot = annotParams[key];
annot.width *= inchToPointFactor;
annot.height *= inchToPointFactor;
annot.left += leftInInch - 0.7;
annot.left *= inchToPointFactor;
annot.top += topInInch - 0.5;
annot.top *= inchToPointFactor;
}
return annotParams;
}

View File

@@ -1 +1 @@
async function setLangAsync(n,t){document.getElementById("selectedFlag").className="fi "+t+" me-2";await fetch(`/lang/${n}`,{method:"POST",headers:{"Content-Type":"application/json"}})}async function setLanguage(n){const t=await fetch("/lang",{method:"GET",headers:{"Content-Type":"application/json"}}).then(n=>n.json()).then(t=>t.includes(n)).catch(()=>!1);if(t)return await fetch(`/lang/${n}`,{method:"POST",headers:{"Content-Type":"application/json"}}).then(n=>{if(n.redirected)window.location.href=n.url;else if(!n.ok)return Promise.reject("Failed to set language")})}async function logout(){return await fetch(`/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"}}).then(n=>{n.ok&&(window.location.href="/")})}function getAnnotationParams(n=0,t=0,i=72){return fetch(`${window.location.origin}/api/Config/Annotations`,{credentials:"include",method:"GET"}).then(n=>n.json()).then(r=>{var f,u;for(f in r)u=r[f],u.width*=i,u.height*=i,u.left+=n,u.left*=i,u.top+=t,u.top*=i;return r})}class Network{async getEnvelope(n){return this.getRequest(`/api/envelope/${n}`).then(this.wrapJsonResponse.bind(this))}async postEnvelope(n,t,i){return this.postRequest(`/api/envelope/${n}?index=${t}`,i).then(this.wrapJsonResponse.bind(this))}async getDocument(n,t){return this.getRequest(`/api/document/${n}?index=${t}`).then(this.wrapBinaryResponse.bind(this))}async openDocument(n){return this.postRequest(`/api/document/${n}`,{}).then(this.wrapJsonResponse.bind(this))}withCSRFToken(n){const t=getCSRFToken;let i=n.headers;return n.headers={...i,...t},n}getCSRFToken(){const n=document.getElementsByName("__RequestVerificationToken")[0].value;return{"X-XSRF-TOKEN":n}}getRequest(n){const t=this.getCSRFToken(),i={credentials:"include",method:"GET",headers:{...t}};return fetch(n,i)}postRequest(n,t){const i=this.getCSRFToken(),r={credentials:"include",method:"POST",headers:{...i,"Content-Type":"application/json; charset=utf-8"},body:JSON.stringify(t)};return fetch(n,r)}async wrapJsonResponse(n){return await this.wrapResponse(n,async n=>await n.json())}async wrapBinaryResponse(n){return await this.wrapResponse(n,async n=>await n.arrayBuffer())}async wrapResponse(n,t){let i;if(n.status===200){const r=await t(n);i=new WrappedResponse(r,null)}else if(n.status===403){const t=await n.json();i=new WrappedResponse(null,t)}else i=new WrappedResponse(null,null);return i}}class WrappedResponse{constructor(n,t){this.data=n;this.error=t;this.fatal=n===null&&t===null}}
async function setLangAsync(n,t){document.getElementById("selectedFlag").className="fi "+t+" me-2";await fetch(`/lang/${n}`,{method:"POST",headers:{"Content-Type":"application/json"}})}async function setLanguage(n){const t=await fetch("/lang",{method:"GET",headers:{"Content-Type":"application/json"}}).then(n=>n.json()).then(t=>t.includes(n)).catch(()=>!1);if(t)return await fetch(`/lang/${n}`,{method:"POST",headers:{"Content-Type":"application/json"}}).then(n=>{if(n.redirected)window.location.href=n.url;else if(!n.ok)return Promise.reject("Failed to set language")})}async function logout(){return await fetch(`/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"}}).then(n=>{n.ok&&(window.location.href="/")})}async function getAnnotationParams(n=0,t=0,i=72){var f,r;const u=await fetch(`${window.location.origin}/api/Config/Annotations`,{credentials:"include",method:"GET"}).then(n=>n.json());for(f in u)r=u[f],r.width*=i,r.height*=i,r.left+=n-.7,r.left*=i,r.top+=t-.5,r.top*=i;return u}class Network{async getEnvelope(n){return this.getRequest(`/api/envelope/${n}`).then(this.wrapJsonResponse.bind(this))}async postEnvelope(n,t,i){return this.postRequest(`/api/envelope/${n}?index=${t}`,i).then(this.wrapJsonResponse.bind(this))}async getDocument(n,t){return this.getRequest(`/api/document/${n}?index=${t}`).then(this.wrapBinaryResponse.bind(this))}async openDocument(n){return this.postRequest(`/api/document/${n}`,{}).then(this.wrapJsonResponse.bind(this))}withCSRFToken(n){const t=getCSRFToken;let i=n.headers;return n.headers={...i,...t},n}getCSRFToken(){const n=document.getElementsByName("__RequestVerificationToken")[0].value;return{"X-XSRF-TOKEN":n}}getRequest(n){const t=this.getCSRFToken(),i={credentials:"include",method:"GET",headers:{...t}};return fetch(n,i)}postRequest(n,t){const i=this.getCSRFToken(),r={credentials:"include",method:"POST",headers:{...i,"Content-Type":"application/json; charset=utf-8"},body:JSON.stringify(t)};return fetch(n,r)}async wrapJsonResponse(n){return await this.wrapResponse(n,async n=>await n.json())}async wrapBinaryResponse(n){return await this.wrapResponse(n,async n=>await n.arrayBuffer())}async wrapResponse(n,t){let i;if(n.status===200){const r=await t(n);i=new WrappedResponse(r,null)}else if(n.status===403){const t=await n.json();i=new WrappedResponse(null,t)}else i=new WrappedResponse(null,null);return i}}class WrappedResponse{constructor(n,t){this.data=n;this.error=t;this.fatal=n===null&&t===null}}