Ignore token if dev, pass through request cancellation token, tweak word wrapping

main
exsersewo 2 years ago
parent 42c9abba3f
commit 4ee98fa551
Signed by: exsersewo
GPG Key ID: 9C6DDF9AC9BA14A0
  1. 2
      src/Attributes/RequireTokenAttribute.cs
  2. 2
      src/Controllers/ImageGenerationController.cs
  3. 4
      src/Controllers/ImageManipulationController.cs
  4. 29
      src/Helpers/RequestHelper.cs
  5. 29
      src/Helpers/StreamResult.cs
  6. 1
      src/Kynareth.csproj
  7. 136
      src/Managers/ImageManager.Generation.cs
  8. 30
      src/Managers/ImageManager.Magik.cs
  9. 56
      src/Managers/ImageManager.Manipulation.cs
  10. 83
      src/Managers/ImageManager.cs
  11. 1
      src/Program.cs
  12. 2
      src/appsettings.json

@ -20,7 +20,7 @@ public class RequireTokenAttribute : ActionFilterAttribute
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
var invalidRequest = EventResult.FromFailure("Unauthorized request"); var invalidRequest = EventResult.FromFailure("Unauthorized request");
await context.HttpContext.Response.WriteAsync(invalidRequest.ToJson()); await context.HttpContext.Response.WriteAsync(invalidRequest.ToJson(), context.HttpContext.RequestAborted);
return; return;
} }
} }

@ -34,7 +34,7 @@ public class ImageGenerationController : BaseController<ImageGenerationControlle
{ {
try try
{ {
var result = await ImageManager.GenerateImageAsync(template, variant, sources); var result = await ImageManager.GenerateImageAsync(template, variant, sources, Request.HttpContext.RequestAborted);
if (result is byte[] data) if (result is byte[] data)
{ {

@ -28,7 +28,7 @@ public class ImageManipulationController : BaseController<ImageManipulationContr
{ {
if (!string.IsNullOrWhiteSpace(image)) if (!string.IsNullOrWhiteSpace(image))
{ {
var (data, contentType) = await ImageManager.GetLiquidRescaledImageAsync(image); var (data, contentType) = await ImageManager.GetLiquidRescaledImageAsync(image, HttpContext.RequestAborted);
return HttpContext.SendStream(data, contentType); return HttpContext.SendStream(data, contentType);
} }
@ -68,7 +68,7 @@ public class ImageManipulationController : BaseController<ImageManipulationContr
{ {
if (!string.IsNullOrWhiteSpace(template)) if (!string.IsNullOrWhiteSpace(template))
{ {
var result = await ImageManager.GenerateMemeImageAsync(template, text); var result = await ImageManager.GenerateMemeImageAsync(template, text, HttpContext.RequestAborted);
if (result is byte[] data) if (result is byte[] data)
{ {

@ -6,6 +6,13 @@ namespace Kynareth.Helpers;
public static class RequestHelper public static class RequestHelper
{ {
private static WebApplication _webApplication;
public static void Configure(WebApplication webApp)
{
_webApplication = webApp;
}
public static string GetUrlHostname(this HttpContext context) public static string GetUrlHostname(this HttpContext context)
{ {
var origin = context.Request.Headers["X-Original-Host"]; var origin = context.Request.Headers["X-Original-Host"];
@ -15,17 +22,27 @@ public static class RequestHelper
public static async Task<bool> IsRequestAuthenticatedAsync(this HttpRequest request) public static async Task<bool> IsRequestAuthenticatedAsync(this HttpRequest request)
{ {
string key = request.GetToken(); if (_webApplication.Environment.IsDevelopment()) return true;
while (true)
{
if(request.HttpContext.RequestAborted.IsCancellationRequested)
{
return false;
}
string key = request.GetToken();
if (string.IsNullOrWhiteSpace(key)) return false; if (string.IsNullOrWhiteSpace(key)) return false;
await using var database = request.HttpContext.RequestServices.GetRequiredService<KynarethDbContext>(); await using var database = request.HttpContext.RequestServices.GetRequiredService<KynarethDbContext>();
var tokenEntry = database.Tokens.FirstOrDefault(token => token.Token.Equals(key)); var tokenEntry = database.Tokens.FirstOrDefault(token => token.Token.Equals(key));
if (tokenEntry is not { IsValid: true }) return false; if (tokenEntry is not { IsValid: true }) return false;
return true; return true;
}
} }
public static string GetToken(this HttpRequest request) public static string GetToken(this HttpRequest request)

@ -24,24 +24,31 @@ public class StreamResult : ActionResult, IStatusCodeActionResult, IActionResult
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
var response = context.HttpContext.Response; try
{
var response = context.HttpContext.Response;
response.StatusCode = StatusCode ?? 200; response.StatusCode = StatusCode ?? 200;
response.ContentType = !string.IsNullOrEmpty(ContentType) response.ContentType = !string.IsNullOrEmpty(ContentType)
? ContentType ? ContentType
: "image/png"; : "image/png";
Value.Position = 0; Value.Position = 0;
await Value.CopyToAsync(response.Body); await Value.CopyToAsync(response.Body, context.HttpContext.RequestAborted);
await Value.FlushAsync(); await Value.FlushAsync(context.HttpContext.RequestAborted);
await Value.DisposeAsync(); await Value.DisposeAsync();
context.HttpContext.Response.RegisterForDisposeAsync(Value); context.HttpContext.Response.RegisterForDisposeAsync(Value);
GC.Collect(); GC.Collect();
}
catch (TaskCanceledException)
{
//assume that request was cancelled if this is invoked
}
} }
} }

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Exsersewo.Common" Version="2023.1.14" /> <PackageReference Include="Exsersewo.Common" Version="2023.1.14" />
<PackageReference Include="ICU4N" Version="60.1.0-alpha.356" />
<PackageReference Include="JwtSharp" Version="1.1.0" /> <PackageReference Include="JwtSharp" Version="1.1.0" />
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="12.2.2" /> <PackageReference Include="Magick.NET-Q8-AnyCPU" Version="12.2.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.2" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.2" />

@ -16,93 +16,111 @@ public static partial class ImageManager
GenerationEndpoints = configuration.GetSection("ShitpostBot:Templates").Get<List<ImageGenerationEndpoint>>(); GenerationEndpoints = configuration.GetSection("ShitpostBot:Templates").Get<List<ImageGenerationEndpoint>>();
} }
public static async Task<object> GenerateImageAsync(string template, string variant, string[] sources) public static async Task<object> GenerateImageAsync(string template, string variant, string[] sources, CancellationToken cancellationToken)
{ {
ImageGenerationImage imageTemplate = null; while (true)
var endpoint = GenerationEndpoints.FirstOrDefault(e => e.Name.ToLowerInvariant().Equals(template.ToLowerInvariant()));
if (endpoint is null)
{ {
var ex = new ArgumentException($"Couldn't find endpoint named \"{template}\"", nameof(template)); if(cancellationToken.IsCancellationRequested)
{
return null;
}
return GetResult(StatusCodes.Status404NotFound, EventResult.FromFailureException(ex.Message, ex)); ImageGenerationImage imageTemplate = null;
}
if (endpoint.SourcesRequired > sources.Length) var endpoint = GenerationEndpoints.FirstOrDefault(e => e.Name.ToLowerInvariant().Equals(template.ToLowerInvariant()));
{
var ex = new ArgumentException("Not enough sources provided", nameof(sources));
return GetResult(StatusCodes.Status400BadRequest, EventResult.FromFailureException(ex.Message, ex)); if (endpoint is null)
} {
var ex = new ArgumentException($"Couldn't find endpoint named \"{template}\"", nameof(template));
if (!string.IsNullOrWhiteSpace(variant)) return GetResult(StatusCodes.Status404NotFound, EventResult.FromFailureException(ex.Message, ex));
{ }
imageTemplate = endpoint.Variants?
.FirstOrDefault(v => v.Name.ToLowerInvariant().Equals(variant.ToLowerInvariant()));
if (imageTemplate is null) if (endpoint.SourcesRequired > sources.Length)
{ {
var ex = new ArgumentException( var ex = new ArgumentException("Not enough sources provided", nameof(sources));
$"Invalid variant given \"{variant}\" for endpoint \"{template}\"",
nameof(variant)
);
return GetResult(StatusCodes.Status400BadRequest, EventResult.FromFailureException(ex.Message, ex)); return GetResult(StatusCodes.Status400BadRequest, EventResult.FromFailureException(ex.Message, ex));
} }
}
else
{
imageTemplate = endpoint;
}
string image = Path.Combine(GenerationTemplateBaseFolder, !string.IsNullOrWhiteSpace(endpoint.Folder) ? endpoint.Folder : "", imageTemplate.Image); if (!string.IsNullOrWhiteSpace(variant))
{
using MagickImage templateImage = new(System.IO.File.ReadAllBytes(image)); imageTemplate = endpoint.Variants?
using MagickImage baseImage = new(MagickColors.Transparent, templateImage.Width, templateImage.Height); .FirstOrDefault(v => v.Name.ToLowerInvariant().Equals(variant.ToLowerInvariant()));
if (imageTemplate is null)
{
var ex = new ArgumentException(
$"Invalid variant given \"{variant}\" for endpoint \"{template}\"",
nameof(variant)
);
return GetResult(StatusCodes.Status400BadRequest, EventResult.FromFailureException(ex.Message, ex));
}
}
else
{
imageTemplate = endpoint;
}
baseImage.Format = MagickFormat.Png; string image = Path.Combine(GenerationTemplateBaseFolder, !string.IsNullOrWhiteSpace(endpoint.Folder) ? endpoint.Folder : "", imageTemplate.Image);
if (imageTemplate.PlaceUnder) using MagickImage templateImage = new(System.IO.File.ReadAllBytes(image));
{ using MagickImage baseImage = new(MagickColors.Transparent, templateImage.Width, templateImage.Height);
await AddSourcesToImage(baseImage, imageTemplate, sources);
}
baseImage.Composite(templateImage, Gravity.Northwest, CompositeOperator.Over);
if (!imageTemplate.PlaceUnder) baseImage.Format = MagickFormat.Png;
{
await AddSourcesToImage(baseImage, imageTemplate, sources); if (imageTemplate.PlaceUnder)
{
await AddSourcesToImage(baseImage, imageTemplate, sources, cancellationToken);
}
baseImage.Composite(templateImage, Gravity.Northwest, CompositeOperator.Over);
if (!imageTemplate.PlaceUnder)
{
await AddSourcesToImage(baseImage, imageTemplate, sources, cancellationToken);
}
return baseImage.ToByteArray();
} }
return baseImage.ToByteArray(); return null;
} }
static async Task AddSourcesToImage(MagickImage image, ImageGenerationImage template, IEnumerable<string> sources) static async Task AddSourcesToImage(MagickImage image, ImageGenerationImage template, IEnumerable<string> sources, CancellationToken cancellationToken)
{ {
int img = 0; while (true)
foreach (var src in sources)
{ {
var position = template.Positions.ElementAtOrDefault(img); if(cancellationToken.IsCancellationRequested)
{
return;
}
int img = 0;
foreach (var src in sources)
{
var position = template.Positions.ElementAtOrDefault(img);
using MagickImage srcImg = new MagickImage(await HttpWebClient.GetStreamAsync(new Uri(src))); using MagickImage srcImg = new MagickImage(await HttpWebClient.GetStreamAsync(new Uri(src)));
srcImg.Resize(position.Width, position.Height); srcImg.Resize(position.Width, position.Height);
if (position.Rotation != 0) if (position.Rotation != 0)
{ {
srcImg.Rotate(template.Rotate > 0 ? -position.Rotation : position.Rotation); srcImg.Rotate(template.Rotate > 0 ? -position.Rotation : position.Rotation);
} }
string background = !string.IsNullOrWhiteSpace(position.Background) ? $"#{position.Background}" : MagickColors.Transparent.ToHexString(); string background = !string.IsNullOrWhiteSpace(position.Background) ? $"#{position.Background}" : MagickColors.Transparent.ToHexString();
var tmp = new MagickImage($"xc:{background}", position.Width, position.Height); var tmp = new MagickImage($"xc:{background}", position.Width, position.Height);
tmp.Composite(srcImg, Gravity.Center, 0, 0, CompositeOperator.Over); tmp.Composite(srcImg, Gravity.Center, 0, 0, CompositeOperator.Over);
image.Composite(tmp, Gravity.Northwest, position.X, position.Y, CompositeOperator.Over); image.Composite(tmp, Gravity.Northwest, position.X, position.Y, CompositeOperator.Over);
img++; img++;
}
} }
} }

@ -9,27 +9,35 @@ public static partial class ImageManager
readonly static Encoding s_defaultEncoding = Encoding.Unicode; readonly static Encoding s_defaultEncoding = Encoding.Unicode;
const double c_defaultFontSize = 20; const double c_defaultFontSize = 20;
public static async Task<(MemoryStream data, string contentType)> GetLiquidRescaledImageAsync(string image) public static async Task<(MemoryStream data, string contentType)> GetLiquidRescaledImageAsync(string image, CancellationToken cancellationToken)
{ {
var imageStream = await HttpWebClient.GetStreamAsync(new Uri(image)).ConfigureAwait(false); while (true)
{
if (cancellationToken.IsCancellationRequested)
{
return (null, null);
}
var imageStream = await HttpWebClient.GetStreamAsync(new Uri(image)).ConfigureAwait(false);
using var magikImage = new MagickImageCollection(imageStream); using var magikImage = new MagickImageCollection(imageStream);
Parallel.ForEach(magikImage, LiquidResizeFrame); Parallel.ForEach(magikImage, LiquidResizeFrame);
MemoryStream stream = new(); MemoryStream stream = new();
magikImage.Write(stream); magikImage.Write(stream);
stream.Position = 0; stream.Position = 0;
magikImage.Dispose(); magikImage.Dispose();
string cType = "image/png"; string cType = "image/png";
if (magikImage.Count > 1) cType = "image/gif"; if (magikImage.Count > 1) cType = "image/gif";
return (stream, cType); return (stream, cType);
}
} }
static void LiquidResizeFrame(IMagickImage<byte> frame) static void LiquidResizeFrame(IMagickImage<byte> frame)

@ -26,40 +26,48 @@ public static partial class ImageManager
}); });
} }
public static async Task<object> GenerateMemeImageAsync(string template, string[] texts) public static async Task<object> GenerateMemeImageAsync(string template, string[] texts, CancellationToken cancellationToken)
{ {
ImageManipulationImage imageTemplate = ManipulationEndpoints.FirstOrDefault(e => e.Name.ToLowerInvariant().Equals(template.ToLowerInvariant())); while (true)
if (imageTemplate is null)
{ {
var ex = new ArgumentException($"Couldn't find endpoint named \"{template}\"", nameof(template)); if (cancellationToken.IsCancellationRequested)
{
return null;
}
ImageManipulationImage imageTemplate = ManipulationEndpoints.FirstOrDefault(e => e.Name.ToLowerInvariant().Equals(template.ToLowerInvariant()));
if (imageTemplate is null)
{
var ex = new ArgumentException($"Couldn't find endpoint named \"{template}\"", nameof(template));
return GetResult(StatusCodes.Status404NotFound, EventResult.FromFailureException(ex.Message, ex)); return GetResult(StatusCodes.Status404NotFound, EventResult.FromFailureException(ex.Message, ex));
} }
if (imageTemplate.TextPositions.Count > texts.Length) if (imageTemplate.TextPositions.Count > texts.Length)
{ {
var ex = new ArgumentException("Not enough texts provided", nameof(texts)); var ex = new ArgumentException("Not enough texts provided", nameof(texts));
return GetResult(StatusCodes.Status400BadRequest, EventResult.FromFailureException(ex.Message, ex)); return GetResult(StatusCodes.Status400BadRequest, EventResult.FromFailureException(ex.Message, ex));
} }
string image = Path.Combine(ManipulationTemplateBaseFolder, imageTemplate.Image); string image = Path.Combine(ManipulationTemplateBaseFolder, imageTemplate.Image);
using MagickImage templateImage = new(File.ReadAllBytes(image)); using MagickImage templateImage = new(File.ReadAllBytes(image));
using MagickImage baseImage = new(MagickColors.Transparent, templateImage.Width, templateImage.Height); using MagickImage baseImage = new(MagickColors.Transparent, templateImage.Width, templateImage.Height);
baseImage.Format = MagickFormat.Png; baseImage.Format = MagickFormat.Png;
baseImage.Composite(templateImage, CompositeOperator.Over); baseImage.Composite(templateImage, CompositeOperator.Over);
int textPoint = 0; int textPoint = 0;
foreach (var textArea in imageTemplate.TextPositions) foreach (var textArea in imageTemplate.TextPositions)
{ {
baseImage.WriteText(texts[textPoint], textArea, MagickColors.White, Encoding.Unicode); baseImage.WriteText(texts[textPoint], textArea, MagickColors.White, Encoding.Unicode);
textPoint++; textPoint++;
} }
return baseImage.ToByteArray(); return baseImage.ToByteArray();
}
} }
} }

@ -1,5 +1,7 @@
using System.Globalization;
using System.Numerics; using System.Numerics;
using System.Text; using System.Text;
using ICU4N.Text;
using ImageMagick; using ImageMagick;
using Kynareth.Models; using Kynareth.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -14,38 +16,63 @@ public static partial class ImageManager
ConfigureManipulation(configuration, appEnvironment); ConfigureManipulation(configuration, appEnvironment);
} }
private static ObjectResult GetResult(int statusCode, object data) static BreakIterator lineBreakIterator = BreakIterator.GetLineInstance(CultureInfo.InvariantCulture);
=> new(data) { StatusCode = statusCode };
public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) private static ObjectResult GetResult(int statusCode, object data) => new(data) { StatusCode = statusCode };
private static string GetText(string text)
{ {
using var label = new MagickImage($"label:{text}", new MagickReadSettings() StringBuilder newText = new();
// set the text to be broken
lineBreakIterator.SetText(text);
// get the first boundary
int start = lineBreakIterator.First();
// iterate over all boundaries
for (int end = lineBreakIterator.Next(); end != BreakIterator.Done; start = end, end = lineBreakIterator.Next())
{
// get the current line
string line = text.Substring(start, end - start);
// do something with the line
newText.AppendLine(line);
Console.WriteLine(line);
}
return newText.ToString();
}
public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{
using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()
{ {
Width = rect.Width, Width = rect.Width,
Height = rect.Height, Height = rect.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = textGravity,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black),
StrokeWidth = strokeWidth StrokeWidth = strokeWidth,
}); });
image.Composite(label, rect.X, rect.Y, CompositeOperator.Over); image.Composite(label, rect.X, rect.Y, CompositeOperator.Over);
return image; return image;
} }
public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{ {
using var label = new MagickImage($"caption:{text}", new MagickReadSettings() using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()
{ {
Width = rect.Width, Width = rect.Width,
Height = rect.Height, Height = rect.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = textGravity,
FontPointsize = fontSize, FontPointsize = fontSize,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
@ -58,15 +85,15 @@ public static partial class ImageManager
return image; return image;
} }
public static MagickImage WriteText(this MagickImage image, string text, Vector2 position, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) public static MagickImage WriteText(this MagickImage image, string text, Vector2 position, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{ {
using var label = new MagickImage($"caption:{text}", new MagickReadSettings() using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()
{ {
Width = image.Width, Width = image.Width,
Height = image.Height, Height = image.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = textGravity,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black),
@ -78,15 +105,15 @@ public static partial class ImageManager
return image; return image;
} }
public static MagickImage WriteText(this MagickImage image, string text, Vector2 position, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) public static MagickImage WriteText(this MagickImage image, string text, Vector2 position, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{ {
using var label = new MagickImage($"caption:{text}", new MagickReadSettings() using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()
{ {
Width = image.Width, Width = image.Width,
Height = image.Height, Height = image.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = textGravity,
FontPointsize = fontSize, FontPointsize = fontSize,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
@ -99,15 +126,15 @@ public static partial class ImageManager
return image; return image;
} }
public static MagickImage WriteText(this MagickImage image, string text, PointD position, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) public static MagickImage WriteText(this MagickImage image, string text, PointD position, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{ {
using var label = new MagickImage($"caption:{text}", new MagickReadSettings() using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()
{ {
Width = image.Width, Width = image.Width,
Height = image.Height, Height = image.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = textGravity,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black),
@ -119,15 +146,15 @@ public static partial class ImageManager
return image; return image;
} }
public static MagickImage WriteText(this MagickImage image, string text, PointD position, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) public static MagickImage WriteText(this MagickImage image, string text, PointD position, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{ {
using var label = new MagickImage($"caption:{text}", new MagickReadSettings() using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()
{ {
Width = image.Width, Width = image.Width,
Height = image.Height, Height = image.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = textGravity,
FontPointsize = fontSize, FontPointsize = fontSize,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
@ -140,15 +167,15 @@ public static partial class ImageManager
return image; return image;
} }
public static MagickImage WriteText(this MagickImage image, string text, int x, int y, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) public static MagickImage WriteText(this MagickImage image, string text, int x, int y, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{ {
using var label = new MagickImage($"caption:{text}", new MagickReadSettings() using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()
{ {
Width = image.Width, Width = image.Width,
Height = image.Height, Height = image.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = textGravity,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,
StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black), StrokeColor = new MagickColor(strokeColor ?? MagickColors.Black),
@ -160,15 +187,15 @@ public static partial class ImageManager
return image; return image;
} }
public static MagickImage WriteText(this MagickImage image, string text, int x, int y, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null) public static MagickImage WriteText(this MagickImage image, string text, int x, int y, MagickColor fontColor, Encoding encoding, double fontSize, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{ {
using var label = new MagickImage($"caption:{text}", new MagickReadSettings() using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()
{ {
Width = image.Width, Width = image.Width,
Height = image.Height, Height = image.Height,
BackgroundColor = MagickColors.Transparent, BackgroundColor = MagickColors.Transparent,
FillColor = fontColor, FillColor = fontColor,
TextGravity = Gravity.Center, TextGravity = textGravity,
FontPointsize = fontSize, FontPointsize = fontSize,
Font = font, Font = font,
TextEncoding = encoding, TextEncoding = encoding,

@ -32,6 +32,7 @@ builder.Services.AddDbContext<KynarethDbContext>(options =>
var app = builder.Build(); var app = builder.Build();
RequestHelper.Configure(app);
ImageManager.Configure(app.Configuration, app.Environment); ImageManager.Configure(app.Configuration, app.Environment);
AmogusHelper.Configure(app.Environment); AmogusHelper.Configure(app.Environment);

@ -1702,7 +1702,7 @@
"x": 593, "x": 593,
"y": 411, "y": 411,
"w": 169, "w": 169,
"h": 75 "h": 300
} }
] ]
}, },

Loading…
Cancel
Save