Add AnnotationDto and AnnotationService for API integration
Added `AnnotationDto` to represent signature annotation positions, including properties for ID, page, and coordinates, with detailed documentation on coordinate systems. Implemented `AnnotationService` to fetch annotation data from an API endpoint, using `HttpClient` and supporting cancellation tokens. Registered `AnnotationService` in the DI container to enable its use across the application. Provided development setup details for working with fake data via YARP-proxied routes.
This commit is contained in:
29
EnvelopeGenerator.ReceiverUI/Models/AnnotationDto.cs
Normal file
29
EnvelopeGenerator.ReceiverUI/Models/AnnotationDto.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
namespace EnvelopeGenerator.ReceiverUI.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a pre-assigned signature annotation position on a specific page.
|
||||||
|
/// <br/><br/>
|
||||||
|
/// <b>Coordinate unit (X, Y):</b> Hundredths of an inch (1/100 inch ? 2.83 PDF points),
|
||||||
|
/// origin at the <b>top-left</b> corner of the page, both axes increase downward/rightward.
|
||||||
|
/// This matches the DevExpress XtraReports coordinate system (<see cref="System.Drawing.RectangleF"/>).
|
||||||
|
/// <br/><br/>
|
||||||
|
/// <b>Difference from PSPDFKit:</b> Same origin (top-left) and direction, but PSPDFKit uses PDF points (1/72 inch).
|
||||||
|
/// Convert: <c>xDX = xPsPdf * (100.0 / 72.0)</c>
|
||||||
|
/// <br/>
|
||||||
|
/// <b>Difference from GDPicture:</b> GDPicture uses PDF points with <b>bottom-left</b> origin (PDF standard); Y is flipped.
|
||||||
|
/// Convert: <c>yDX = (pageHeightPt - yGD - elemHeightPt) * (100.0 / 72.0)</c>
|
||||||
|
/// </summary>
|
||||||
|
public record AnnotationDto
|
||||||
|
{
|
||||||
|
/// <summary>Unique identifier of the annotation.</summary>
|
||||||
|
public long Id { get; init; }
|
||||||
|
|
||||||
|
/// <summary>1-based page number within the document.</summary>
|
||||||
|
public int? Page { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Horizontal position in hundredths of an inch from the left edge of the page.</summary>
|
||||||
|
public double? X { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Vertical position in hundredths of an inch from the top edge of the page.</summary>
|
||||||
|
public double? Y { get; init; }
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ builder.Services.Configure<ApiOptions>(opts =>
|
|||||||
builder.Configuration.GetSection(ApiOptions.SectionName).Bind(opts));
|
builder.Configuration.GetSection(ApiOptions.SectionName).Bind(opts));
|
||||||
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.DocumentService>();
|
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.DocumentService>();
|
||||||
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.AuthService>();
|
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.AuthService>();
|
||||||
|
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.AnnotationService>();
|
||||||
|
|
||||||
builder.Services.AddDevExpressWebAssemblyBlazorReportViewer();
|
builder.Services.AddDevExpressWebAssemblyBlazorReportViewer();
|
||||||
builder.Services.AddDevExpressWebAssemblyBlazorPdfViewer();
|
builder.Services.AddDevExpressWebAssemblyBlazorPdfViewer();
|
||||||
|
|||||||
32
EnvelopeGenerator.ReceiverUI/Services/AnnotationService.cs
Normal file
32
EnvelopeGenerator.ReceiverUI/Services/AnnotationService.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json;
|
||||||
|
using EnvelopeGenerator.ReceiverUI.Models;
|
||||||
|
using EnvelopeGenerator.ReceiverUI.Options;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.ReceiverUI.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves annotation positions from the API.
|
||||||
|
/// The URL is composed as <c>{BaseUrl}/api/Annotation/{envelopeKey}</c>.
|
||||||
|
/// During development, <c>BaseUrl</c> is empty so the request resolves to the
|
||||||
|
/// YARP-proxied route on the same origin, which currently serves
|
||||||
|
/// <c>fake-data/annotations.json</c>. To switch to real data, update the
|
||||||
|
/// YARP route in <c>yarp.json</c> — no code change required.
|
||||||
|
/// </summary>
|
||||||
|
public class AnnotationService(HttpClient http, IOptions<ApiOptions> apiOptions)
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web);
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<AnnotationDto>> GetAnnotationsAsync(string envelopeKey, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var url = $"{apiOptions.Value.BaseUrl}/api/Annotation/{Uri.EscapeDataString(envelopeKey)}";
|
||||||
|
var response = await http.GetAsync(url, cancel);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var result = await response.Content.ReadFromJsonAsync<List<AnnotationDto>>(_jsonOptions, cancel);
|
||||||
|
return result ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user