Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Avolutions.Baf.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Nullable>enable</Nullable>

<PackageId>Avolutions.Baf.Core</PackageId>
<Version>0.21.0</Version>
<Version>0.22.0</Version>

<Title>Avolutions BAF Core</Title>
<Company>Avolutions</Company>
Expand Down
9 changes: 7 additions & 2 deletions src/Template/Abstractions/ITemplateService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
namespace Avolutions.Baf.Core.Template.Abstractions;

public interface ITemplateService<in TTemplate, TResult>
public interface ITemplateService
{
Task<TResult> ApplyModelToTemplateAsync(TTemplate template, object model, CancellationToken ct);
IReadOnlyList<string> ExtractFieldNames(Stream template);
}

public interface ITemplateService<in TTemplate, TResult> : ITemplateService
{
Task<TResult> ApplyModelToTemplateAsync(TTemplate template, object model, CancellationToken ct);
Task<TResult> ApplyValuesToTemplateAsync(TTemplate template, IDictionary<string, string> values, CancellationToken ct);
}
7 changes: 7 additions & 0 deletions src/Template/Abstractions/ITemplateServiceResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Avolutions.Baf.Core.Template.Abstractions;

public interface ITemplateServiceResolver
{
ITemplateService GetFieldExtractor(string extension);
ITemplateService<TTemplate, TResult> GetTemplateService<TTemplate, TResult>(string extension);
}
7 changes: 7 additions & 0 deletions src/Template/Attributes/TemplateExtensionAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Avolutions.Baf.Core.Template.Attributes;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class TemplateExtensionAttribute(string extension) : Attribute
{
public string Extension { get; } = extension;
}
42 changes: 42 additions & 0 deletions src/Template/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Reflection;
using Avolutions.Baf.Core.Template.Abstractions;
using Avolutions.Baf.Core.Template.Attributes;
using Microsoft.Extensions.DependencyInjection;

namespace Avolutions.Baf.Core.Template.Extensions;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddTemplateService<TService>(this IServiceCollection services)
where TService : class, ITemplateService
{
var type = typeof(TService);

var attrs = type.GetCustomAttributes<TemplateExtensionAttribute>().ToList();
if (attrs.Count == 0)
{
throw new InvalidOperationException(
$"{type.Name} is missing the [TemplateExtension] attribute.");
}

var closedGeneric = type.GetInterfaces().FirstOrDefault(i =>
i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(ITemplateService<,>))
?? throw new InvalidOperationException(
$"{type.Name} does not implement ITemplateService<,>.");

services.AddScoped<TService>();

foreach (var attr in attrs)
{
var key = attr.Extension.ToLowerInvariant();

services.AddKeyedScoped<ITemplateService>(key,
(sp, _) => sp.GetRequiredService<TService>());
services.AddKeyedScoped(closedGeneric, key,
(sp, _) => sp.GetRequiredService<TService>());
}

return services;
}
}
25 changes: 23 additions & 2 deletions src/Template/Services/PdfTemplateService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using PdfSharp;
using Avolutions.Baf.Core.Template.Attributes;
using PdfSharp;
using PdfSharp.Fonts;
using PdfSharp.Pdf;
using PdfSharp.Pdf.AcroForms;
using PdfSharp.Pdf.IO;

namespace Avolutions.Baf.Core.Template.Services;

[TemplateExtension(".pdf")]
public class PdfTemplateService : TemplateService<Stream, byte[]>
{
private static bool _fontsConfigured;
Expand All @@ -21,7 +23,26 @@ public PdfTemplateService()

public override IReadOnlyList<string> ExtractFieldNames(Stream template)
{
throw new NotImplementedException();
var fieldNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

using var document = PdfReader.Open(template, PdfDocumentOpenMode.Modify);

var form = document.AcroForm;
if (form.Fields.Count == 0)
{
return [];
}

var fields = form.Fields;
foreach (string fieldName in fields.DescendantNames)
{
if (fields[fieldName] is PdfTextField { ReadOnly: false })
{
fieldNames.Add(GetSimpleName(fieldName));
}
}

return fieldNames.ToList();
}

public override Task<byte[]> ApplyValuesToTemplateAsync(Stream template, IDictionary<string, string> values, CancellationToken ct)
Expand Down
33 changes: 33 additions & 0 deletions src/Template/Services/TemplateServiceResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Avolutions.Baf.Core.Template.Abstractions;
using Microsoft.Extensions.DependencyInjection;

namespace Avolutions.Baf.Core.Template.Services;

public class TemplateServiceResolver(IServiceProvider serviceProvider) : ITemplateServiceResolver
{
public ITemplateService GetFieldExtractor(string extension) =>
serviceProvider.GetKeyedService<ITemplateService>(Normalize(extension))
?? throw new NotSupportedException(
$"No template service registered for '{extension}'.");

public ITemplateService<TTemplate, TResult> GetTemplateService<TTemplate, TResult>(string extension)
{
var key = Normalize(extension);

var service = serviceProvider.GetKeyedService<ITemplateService<TTemplate, TResult>>(key);
if (service is not null)
{
return service;
}

// Distinguish "unknown extension" from "wrong type parameters" for a useful error.
throw serviceProvider.GetKeyedService<ITemplateService>(key) is not null
? new InvalidOperationException(
$"Template service for '{extension}' does not support " +
$"{typeof(TTemplate).Name} -> {typeof(TResult).Name}.")
: new NotSupportedException(
$"No template service registered for '{extension}'.");
}

private static string Normalize(string extension) => extension.ToLowerInvariant();
}
4 changes: 3 additions & 1 deletion src/Template/Services/WordTemplateService.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using DocumentFormat.OpenXml;
using Avolutions.Baf.Core.Template.Attributes;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace Avolutions.Baf.Core.Template.Services;

[TemplateExtension(".docx")]
public class WordTemplateService : TemplateService<Stream, byte[]>
{
public override IReadOnlyList<string> ExtractFieldNames(Stream template)
Expand Down
7 changes: 5 additions & 2 deletions src/Template/TemplateModule.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Avolutions.Baf.Core.Module.Abstractions;
using Avolutions.Baf.Core.Template.Abstractions;
using Avolutions.Baf.Core.Template.Extensions;
using Avolutions.Baf.Core.Template.Services;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -9,7 +11,8 @@ public class TemplateModule : IFeatureModule
public void Register(IServiceCollection services)
{
services.AddSingleton<HandlebarsTemplateService>();
services.AddSingleton<PdfTemplateService>();
services.AddSingleton<WordTemplateService>();
services.AddScoped<ITemplateServiceResolver, TemplateServiceResolver>();
services.AddTemplateService<PdfTemplateService>();
services.AddTemplateService<WordTemplateService>();
}
}
Loading