Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace ImageSharp library #18291

Merged
merged 2 commits into from
Nov 30, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
using System;
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using ImageMagick;

namespace Volo.CmsKit.Public.Web.Security.Captcha;

public class CaptchaOptions
{
public Color[] TextColor { get; set; } = new Color[]
public MagickColor[] TextColor { get; set; } = new MagickColor[]
{
Color.Blue, Color.Black, Color.Black, Color.Brown, Color.Gray, Color.Green
MagickColors.Blue, MagickColors.Black, MagickColors.Black, MagickColors.Brown, MagickColors.Gray, MagickColors.Green
};
public Color[] DrawLinesColor { get; set; } = new Color[]
public MagickColor[] DrawLinesColor { get; set; } = new MagickColor[]
{
Color.Blue, Color.Black, Color.Black, Color.Brown, Color.Gray, Color.Green
MagickColors.Blue, MagickColors.Black, MagickColors.Black, MagickColors.Brown, MagickColors.Gray, MagickColors.Green
};

public float MinLineThickness { get; set; } = 0.7f;
Expand All @@ -26,15 +24,15 @@ public class CaptchaOptions

public ushort NoiseRate { get; set; } = 500;

public Color[] NoiseRateColor { get; set; } = new Color[] { Color.Gray };
public MagickColor[] NoiseRateColor { get; set; } = new MagickColor[] { MagickColors.Gray };

public byte FontSize { get; set; } = 32;

public FontStyle FontStyle { get; set; } = FontStyle.Regular;
public FontStyleType FontStyle { get; set; } = FontStyleType.Normal;

public EncoderTypes EncoderType { get; set; } = EncoderTypes.Png;

public IImageEncoder Encoder => RandomTextGenerator.GetEncoder(EncoderType);
public MagickFormat Encoder => RandomTextGenerator.GetEncoder(EncoderType);

public byte DrawLines { get; set; } = 2;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
using System;
using System.Security.Cryptography;
using System.Text;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using ImageMagick;

namespace Volo.CmsKit.Public.Web.Security.Captcha;
public static class RandomTextGenerator
{
private static readonly char[] AllowedChars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVXYZW23456789".ToCharArray();

public static IImageEncoder GetEncoder(EncoderTypes encoderType)
public static MagickFormat GetEncoder(EncoderTypes encoderType)
{
IImageEncoder encoder = encoderType switch
var encoder = encoderType switch
{
EncoderTypes.Png => new PngEncoder(),
EncoderTypes.Jpeg => new JpegEncoder(),
EncoderTypes.Png => MagickFormat.Png,
EncoderTypes.Jpeg => MagickFormat.Jpeg,
_ => throw new ArgumentException($"Encoder '{encoderType}' not found!")
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
using System;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Volo.Abp;
using Volo.CmsKit.Localization;
using Microsoft.Extensions.Localization;
using Volo.Abp.DependencyInjection;
using Color = SixLabors.ImageSharp.Color;
using PointF = SixLabors.ImageSharp.PointF;
using Volo.Abp.Caching;
using Microsoft.Extensions.Caching.Distributed;
using ImageMagick;

namespace Volo.CmsKit.Public.Web.Security.Captcha;

Expand Down Expand Up @@ -108,93 +100,79 @@ public virtual async Task ValidateAsync(Guid requestId, string value)

private byte[] GenerateInternal(string stringText, CaptchaOptions options)
{
byte[] result;
var random = new Random();
var fontName = MagickNET.FontNames.First();

var drawables = new Drawables()
.Font(fontName, options.FontStyle, FontWeight.Normal, FontStretch.Normal)
.FontPointSize(options.FontSize)
.StrokeColor(MagickColors.Transparent);

var size = (ushort)(drawables.FontTypeMetrics(stringText)?.TextWidth ?? 0);
using var image = new MagickImage(MagickColors.White, size + 15, options.Height);

using (var image = new Image<Rgba32>(options.Width, options.Height))
double position = 0;
var startWith = (byte)random.Next(5, 10);

foreach (var character in stringText)
{
float position = 0;
var random = new Random();
var startWith = (byte)random.Next(5, 10);
image.Mutate(ctx => ctx.BackgroundColor(Color.Transparent));

var fontFamily = SystemFonts.Families
.FirstOrDefault(x => x.GetAvailableStyles().Contains(options.FontStyle), SystemFonts.Families.First())
.Name;

var font = SystemFonts.CreateFont(fontFamily, options.FontSize, options.FontStyle);

foreach (var character in stringText)
{
var text = character.ToString();
var color = options.TextColor[random.Next(0, options.TextColor.Length)];
var location = new PointF(startWith + position, random.Next(6, 13));
image.Mutate(ctx => ctx.DrawText(text, font, color, location));
position += TextMeasurer.MeasureSize(character.ToString(), new TextOptions (font)
{
Origin = location
}).Width;
}
var text = character.ToString();
var color = options.TextColor[random.Next(0, options.TextColor.Length)];
drawables.FillColor(new MagickColor(color.R, color.G, color.B, color.A))
.Text(startWith + position,
RandomTextGenerator.GenerateNextFloat(image.BaseHeight / 2.3, image.BaseHeight / 1.7), text);

//add rotation
var rotation = GetRotation(options);
image.Mutate(ctx => ctx.Transform(rotation));
position += drawables.FontTypeMetrics(text)?.TextWidth ?? 0;
}

// add rotation
var rotation = GetRotation(options);
drawables.Rotation(rotation);

// add the dynamic image to original image
var size = (ushort)TextMeasurer.MeasureSize(stringText, new TextOptions(font)).Width;
var img = new Image<Rgba32>(size + 15, options.Height);
img.Mutate(ctx => ctx.BackgroundColor(Color.White));
drawables.Draw(image);

Parallel.For(0, options.DrawLines, i =>
Parallel.For(0, options.DrawLines, _ =>
{
// ReSharper disable once AccessToDisposedClosure
if (image is { IsDisposed: false })
{
var x0 = random.Next(0, random.Next(0, 30));
var y0 = random.Next(10, img.Height);
var y0 = random.Next(10, image.Height);

var x1 = random.Next(30, img.Width);
var y1 = random.Next(0, img.Height);
var x1 = random.Next(30, image.Width);
var y1 = random.Next(0, image.Height);

img.Mutate(ctx =>
ctx.DrawLine(options.TextColor[random.Next(0, options.TextColor.Length)],
RandomTextGenerator.GenerateNextFloat(options.MinLineThickness, options.MaxLineThickness),
new PointF[] { new PointF(x0, y0), new PointF(x1, y1) })
);
});

img.Mutate(ctx => ctx.DrawImage(image, 0.80f));
image.Draw(new Drawables()
.StrokeColor(options.DrawLinesColor[random.Next(0, options.DrawLinesColor.Length)])
.StrokeWidth(RandomTextGenerator.GenerateNextFloat(options.MinLineThickness,
options.MaxLineThickness))
.Line(x0, y0, x1, y1));
}
});

Parallel.For(0, options.NoiseRate, _ =>
Parallel.For(0, options.NoiseRate, _ =>
{
if (image is { IsDisposed: false })
{
var x0 = random.Next(0, img.Width - 1);
var y0 = random.Next(0, img.Height - 1);
img.Mutate(
ctx => ctx
.DrawLine(options.NoiseRateColor[random.Next(0, options.NoiseRateColor.Length)],
RandomTextGenerator.GenerateNextFloat(0.5, 1.5),
new PointF[] { new Vector2(x0, y0), new Vector2(x0 + 0.005f, y0 + 0.005f) })
var x = random.Next(0, image.Width);
var y = random.Next(0, image.Height);
image.Draw(new Drawables()
.FillColor(options.NoiseRateColor[random.Next(0, options.NoiseRateColor.Length)])
.Point(x, y)
);
});

img.Mutate(x =>
{
x.Resize(options.Width, options.Height);
});

using (var ms = new MemoryStream())
{
img.Save(ms, options.Encoder);
result = ms.ToArray();
}
}
});

return result;
image.Resize(new MagickGeometry(options.Width, options.Height) { IgnoreAspectRatio = true });

return image.ToByteArray(options.Encoder);
}

private static AffineTransformBuilder GetRotation(CaptchaOptions options)
private double GetRotation(CaptchaOptions options)
{
var random = new Random();
var width = random.Next(10, options.Width);
var height = random.Next(10, options.Height);
var pointF = new PointF(width, height);
var rotationDegrees = random.Next(0, options.MaxRotationDegrees);
return new AffineTransformBuilder().PrependRotationDegrees(rotationDegrees, pointF);
return rotationDegrees;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" />
<PackageReference Include="Markdig.Signed" />
<PackageReference Include="HtmlSanitizer" />
<PackageReference Include="SixLabors.ImageSharp"/>
<PackageReference Include="SixLabors.ImageSharp.Drawing" />
<PackageReference Include="Magick.NET-Q16-AnyCPU" />
</ItemGroup>

<ItemGroup>
Expand Down