diff --git a/.github/workflows/_test-integrations.yml b/.github/workflows/_test-integrations.yml
index 5700da9b..3406458e 100644
--- a/.github/workflows/_test-integrations.yml
+++ b/.github/workflows/_test-integrations.yml
@@ -62,7 +62,8 @@ jobs:
os:
- "windows-2022"
dotnet-version:
- - "net482"
+ - "net472"
+ - "net48"
- "net6.0"
- "net10.0"
runs-on: ${{ matrix.os }}
diff --git a/Directory.Build.props b/Directory.Build.props
index 2c9edae4..cdf6059e 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -41,4 +41,44 @@
true
false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mindee.Cli/Commands/PredictCommand.cs b/src/Mindee.Cli/Commands/PredictCommand.cs
index c43154ea..90f471e0 100644
--- a/src/Mindee.Cli/Commands/PredictCommand.cs
+++ b/src/Mindee.Cli/Commands/PredictCommand.cs
@@ -1,6 +1,4 @@
using System.CommandLine;
-using System.CommandLine.Invocation;
-using System.CommandLine.IO;
using System.Text.Json;
using Mindee.Input;
using Mindee.Parsing;
@@ -31,103 +29,134 @@ class PredictCommand : Command
where TPage : IPrediction, new()
where TInferenceModel : Inference, new()
{
+ private readonly Option _outputOption;
+ private readonly Option? _allWordsOption;
+ private readonly Option? _fullTextOption;
+ private readonly Option? _asyncOption;
+ private readonly Argument _pathArgument;
+
public PredictCommand(CommandOptions options)
: base(options.Name, options.Description)
{
- AddOption(new Option(["-o", "--output", "output"],
- "Specify how to output the data. \n" +
- "- summary: a basic summary (default)\n" +
- "- raw: full JSON object\n"));
+ _outputOption = new Option("--output", "-o")
+ {
+ Description = "Specify how to output the data. \n" +
+ "- summary: a basic summary (default)\n" +
+ "- raw: full JSON object\n",
+ DefaultValueFactory = _ => OutputType.Summary
+ };
+ Options.Add(_outputOption);
+
if (options.AllWords)
{
- var option = new Option(["-w", "--all-words", "allWords"],
- "To get all the words in the current document. False by default.");
- AddOption(option);
+ _allWordsOption = new Option("--all-words", "-w")
+ {
+ Description = "To get all the words in the current document. False by default.",
+ DefaultValueFactory = _ => false
+ };
+ Options.Add(_allWordsOption);
}
if (options.FullText)
{
- var option = new Option(["-f", "--full-text", "fullText"],
- "To get all the words in the current document. False by default.");
- AddOption(option);
+ _fullTextOption = new Option("--full-text", "-f")
+ {
+ Description = "To get all the words in the current document. False by default.",
+ DefaultValueFactory = _ => false
+ };
+ Options.Add(_fullTextOption);
}
switch (options.Async)
{
case true when !options.Sync:
{
- // Inject an "option" not changeable by the user.
- // This will set the `Handler.Async` property to always be `true`.
- var option = new Option("async", () => true) { IsHidden = true };
- AddOption(option);
+ _asyncOption = new Option("async")
+ {
+ Hidden = true,
+ DefaultValueFactory = _ => true
+ };
+ Options.Add(_asyncOption);
break;
}
case true when options.Sync:
- AddOption(new Option(["--async"],
- "Process the file asynchronously. False by default."));
+ _asyncOption = new Option("--async")
+ {
+ Description = "Process the file asynchronously. False by default.",
+ DefaultValueFactory = _ => false
+ };
+ Options.Add(_asyncOption);
break;
}
- AddArgument(new Argument("path", "The path of the file to parse"));
+ _pathArgument = new Argument("path") { Description = "The path of the file to parse" };
+ Arguments.Add(_pathArgument);
}
- public new class Handler(MindeeClient mindeeClient) : ICommandHandler
+ public void ConfigureAction(MindeeClient mindeeClient)
{
- private readonly JsonSerializerOptions _jsonSerializerOptions = new() { WriteIndented = true };
-
- public string Path { get; set; } = null!;
- public bool AllWords { get; set; } = false;
- public bool FullText { get; set; } = false;
- public OutputType Output { get; set; } = OutputType.Full;
- public bool Async { get; set; } = false;
+ this.SetAction(parseResult =>
+ {
+ var path = parseResult.GetValue(_pathArgument)!;
+ var allWords = _allWordsOption != null && parseResult.GetValue(_allWordsOption);
+ var fullText = _fullTextOption != null && parseResult.GetValue(_fullTextOption);
+ var output = parseResult.GetValue(_outputOption);
+ var isAsync = _asyncOption != null && parseResult.GetValue(_asyncOption);
+
+ var handler = new Handler(mindeeClient);
+ return handler.InvokeAsync(path, allWords, fullText, output, isAsync).GetAwaiter().GetResult();
+ });
+ }
- public int Invoke(InvocationContext context)
+ public class Handler(MindeeClient mindeeClient)
+ {
+ private readonly JsonSerializerOptions _jsonSerializerOptions = new()
{
- return InvokeAsync(context).GetAwaiter().GetResult();
- }
+ WriteIndented = true,
+ Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ };
- public async Task InvokeAsync(InvocationContext context)
+ public async Task InvokeAsync(string path, bool allWords, bool fullText, OutputType output, bool isAsync)
{
- var options =
- new ParseOptions(Path, AllWords, FullText, Output);
- if (Async)
+ var options = new ParseOptions(path, allWords, fullText, output);
+ if (isAsync)
{
- return await EnqueueAndParseAsync(context, options);
+ return await EnqueueAndParseAsync(options);
}
- return await ParseAsync(context, options);
+ return await ParseAsync(options);
}
- private async Task ParseAsync(InvocationContext context, ParseOptions options)
+ private async Task ParseAsync(ParseOptions options)
{
var response = await mindeeClient.ParseAsync(
new LocalInputSource(options.Path),
- new PredictOptions(AllWords, FullText));
+ new PredictOptions(options.AllWords, options.FullText));
if (response == null)
{
- context.Console.Out.Write("null");
+ await Console.Out.WriteAsync("null");
return 1;
}
- PrintToConsole(context.Console.Out, options, response);
+ PrintToConsole(Console.Out, options, response);
return 0;
}
- private async Task EnqueueAndParseAsync(InvocationContext context, ParseOptions options)
+ private async Task EnqueueAndParseAsync(ParseOptions options)
{
var response = await mindeeClient.EnqueueAndParseAsync(
new LocalInputSource(options.Path),
- new PredictOptions(AllWords, FullText),
+ new PredictOptions(options.AllWords, options.FullText),
null,
new AsyncPollingOptions());
- PrintToConsole(context.Console.Out, options, response);
+ PrintToConsole(Console.Out, options, response);
return 0;
}
private void PrintToConsole(
- IStandardStreamWriter console,
+ TextWriter console,
ParseOptions options,
PredictResponse response)
{
diff --git a/src/Mindee.Cli/Mindee.Cli.csproj b/src/Mindee.Cli/Mindee.Cli.csproj
index 79b608a0..da4ac94b 100644
--- a/src/Mindee.Cli/Mindee.Cli.csproj
+++ b/src/Mindee.Cli/Mindee.Cli.csproj
@@ -7,6 +7,10 @@
enable
+
+
+
+
PreserveNewest
@@ -28,21 +32,8 @@
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
diff --git a/src/Mindee.Cli/Program.cs b/src/Mindee.Cli/Program.cs
index af0e071e..b92645cd 100644
--- a/src/Mindee.Cli/Program.cs
+++ b/src/Mindee.Cli/Program.cs
@@ -1,10 +1,8 @@
using System.CommandLine;
-using System.CommandLine.Builder;
-using System.CommandLine.Hosting;
-using System.CommandLine.Parsing;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Mindee;
using Mindee.Cli.Commands;
-// ReSharper disable once RedundantUsingDirective
using Mindee.Extensions.DependencyInjection;
using PredictBankAccountDetailsCommand = Mindee.Cli.Commands.PredictCommand<
Mindee.Product.Fr.BankAccountDetails.BankAccountDetailsV2,
@@ -82,98 +80,125 @@
Mindee.Product.Receipt.ReceiptV5Document
>;
-var runner = BuildCommandLine()
- .UseHost(_ => Host.CreateDefaultBuilder(args), builder =>
+// Setup dependency injection
+var host = Host.CreateDefaultBuilder(args)
+ .ConfigureServices((_, services) =>
{
- builder
- .ConfigureServices((_, services) =>
- {
- services.AddMindeeClient();
- services.AddMindeeClientV2();
- })
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- .UseCommandHandler()
- ;
+ services.AddMindeeClient();
+ services.AddMindeeClientV2();
})
- .UseHelp()
- .UseParseErrorReporting()
- .CancelOnProcessTermination()
- .UseEnvironmentVariableDirective()
- .UseParseDirective()
- .UseSuggestDirective()
- .UseTypoCorrections()
- .UseExceptionHandler()
.Build();
-return await runner.InvokeAsync(args);
+var root = BuildCommandLine(host.Services);
-static CommandLineBuilder BuildCommandLine()
+return await root.Parse(args).InvokeAsync();
+
+static RootCommand BuildCommandLine(IServiceProvider services)
{
var root = new RootCommand();
- root.AddCommand(new PredictBarcodeReaderCommand(new CommandOptions(
+ var mindeeClient = services.GetRequiredService();
+
+ var barcodeReaderCmd = new PredictBarcodeReaderCommand(new CommandOptions(
"barcode-reader", "Barcode Reader",
- false, false, true, false)));
- root.AddCommand(new PredictCropperCommand(new CommandOptions(
+ false, false, true, false));
+ barcodeReaderCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(barcodeReaderCmd);
+
+ var cropperCmd = new PredictCropperCommand(new CommandOptions(
"cropper", "Cropper",
- false, false, true, false)));
- root.AddCommand(new PredictFinancialDocumentCommand(new CommandOptions(
+ false, false, true, false));
+ cropperCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(cropperCmd);
+
+ var financialDocumentCmd = new PredictFinancialDocumentCommand(new CommandOptions(
"financial-document", "Financial Document",
- true, false, true, true)));
- root.AddCommand(new PredictBankAccountDetailsCommand(new CommandOptions(
+ true, false, true, true));
+ financialDocumentCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(financialDocumentCmd);
+
+ var bankAccountDetailsCmd = new PredictBankAccountDetailsCommand(new CommandOptions(
"fr-bank-account-details", "FR Bank Account Details",
- false, false, true, false)));
- root.AddCommand(new PredictCarteGriseCommand(new CommandOptions(
+ false, false, true, false));
+ bankAccountDetailsCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(bankAccountDetailsCmd);
+
+ var carteGriseCmd = new PredictCarteGriseCommand(new CommandOptions(
"fr-carte-grise", "FR Carte Grise",
- false, false, true, false)));
- root.AddCommand(new PredictHealthCardCommand(new CommandOptions(
+ false, false, true, false));
+ carteGriseCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(carteGriseCmd);
+
+ var healthCardCmd = new PredictHealthCardCommand(new CommandOptions(
"fr-health-card", "FR Health Card",
- false, false, false, true)));
- root.AddCommand(new PredictIdCardCommand(new CommandOptions(
+ false, false, false, true));
+ healthCardCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(healthCardCmd);
+
+ var idCardCmd = new PredictIdCardCommand(new CommandOptions(
"fr-carte-nationale-d-identite", "FR Carte Nationale d'Identité",
- false, false, true, false)));
- root.AddCommand(new PredictPayslipCommand(new CommandOptions(
+ false, false, true, false));
+ idCardCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(idCardCmd);
+
+ var payslipCmd = new PredictPayslipCommand(new CommandOptions(
"fr-payslip", "FR Payslip",
- false, false, false, true)));
- root.AddCommand(new PredictInternationalIdCommand(new CommandOptions(
+ false, false, false, true));
+ payslipCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(payslipCmd);
+
+ var internationalIdCmd = new PredictInternationalIdCommand(new CommandOptions(
"international-id", "International ID",
- false, true, false, true)));
- root.AddCommand(new PredictInvoiceCommand(new CommandOptions(
+ false, true, false, true));
+ internationalIdCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(internationalIdCmd);
+
+ var invoiceCmd = new PredictInvoiceCommand(new CommandOptions(
"invoice", "Invoice",
- true, false, true, true)));
- root.AddCommand(new PredictInvoiceSplitterCommand(new CommandOptions(
+ true, false, true, true));
+ invoiceCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(invoiceCmd);
+
+ var invoiceSplitterCmd = new PredictInvoiceSplitterCommand(new CommandOptions(
"invoice-splitter", "Invoice Splitter",
- false, false, false, true)));
- root.AddCommand(new PredictMultiReceiptsDetectorCommand(new CommandOptions(
+ false, false, false, true));
+ invoiceSplitterCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(invoiceSplitterCmd);
+
+ var multiReceiptsDetectorCmd = new PredictMultiReceiptsDetectorCommand(new CommandOptions(
"multi-receipts-detector", "Multi Receipts Detector",
- false, false, true, false)));
- root.AddCommand(new PredictPassportCommand(new CommandOptions(
+ false, false, true, false));
+ multiReceiptsDetectorCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(multiReceiptsDetectorCmd);
+
+ var passportCmd = new PredictPassportCommand(new CommandOptions(
"passport", "Passport",
- false, false, true, false)));
- root.AddCommand(new PredictReceiptCommand(new CommandOptions(
+ false, false, true, false));
+ passportCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(passportCmd);
+
+ var receiptCmd = new PredictReceiptCommand(new CommandOptions(
"receipt", "Receipt",
- true, false, true, true)));
- root.AddCommand(new PredictBankCheckCommand(new CommandOptions(
+ true, false, true, true));
+ receiptCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(receiptCmd);
+
+ var bankCheckCmd = new PredictBankCheckCommand(new CommandOptions(
"us-bank-check", "US Bank Check",
- false, false, true, false)));
+ false, false, true, false));
+ bankCheckCmd.ConfigureAction(mindeeClient);
+ root.Subcommands.Add(bankCheckCmd);
+
+ var silentOption = new Option("--silent")
+ {
+ Description = "Disables diagnostics output"
+ };
+ root.Options.Add(silentOption);
- root.AddGlobalOption(new Option("--silent", "Disables diagnostics output"));
- root.SetHandler(() =>
+ root.SetAction(parseResult =>
{
- root.InvokeAsync("--help");
+ Console.WriteLine("Please specify a subcommand. Use --help for more information.");
+ return 1;
});
- return new CommandLineBuilder(root);
+ return root;
}
diff --git a/src/Mindee/Extensions/DependencyInjection/ServiceCollectionsExtensions.cs b/src/Mindee/Extensions/DependencyInjection/ServiceCollectionsExtensions.cs
index 280e937c..9214b363 100644
--- a/src/Mindee/Extensions/DependencyInjection/ServiceCollectionsExtensions.cs
+++ b/src/Mindee/Extensions/DependencyInjection/ServiceCollectionsExtensions.cs
@@ -16,10 +16,28 @@
namespace Mindee.Extensions.DependencyInjection
{
#if !NET6_0_OR_GREATER
+ ///
+ /// Wrapper for V1 RestClient to work around lack of keyed services in .NET Framework
+ ///
+ internal sealed class MindeeV1RestClientWrapper : IDisposable
+ {
+ public RestClient Client { get; }
+
+ public MindeeV1RestClientWrapper(RestClient client)
+ {
+ Client = client;
+ }
+
+ public void Dispose()
+ {
+ Client.Dispose();
+ }
+ }
+
///
/// Wrapper for V2 RestClient to work around lack of keyed services in .NET Framework
///
- internal sealed class MindeeV2RestClientWrapper
+ internal sealed class MindeeV2RestClientWrapper : IDisposable
{
public RestClient Client { get; }
@@ -27,6 +45,11 @@ public MindeeV2RestClientWrapper(RestClient client)
{
Client = client;
}
+
+ public void Dispose()
+ {
+ Client.Dispose();
+ }
}
#endif
@@ -53,7 +76,11 @@ public static void AddMindeeApi(
services.AddSingleton(serviceProvider =>
{
var settings = serviceProvider.GetRequiredService>();
- var restClient = serviceProvider.GetRequiredService();
+#if NET6_0_OR_GREATER
+ var restClient = serviceProvider.GetRequiredService();
+#else
+ var restClient = serviceProvider.GetRequiredService().Client;
+#endif
var logger = serviceProvider.GetService()?.CreateLogger();
return new MindeeApi(settings, restClient, logger);
});
@@ -101,6 +128,7 @@ public static void AddMindeeApiV2(
private static void RegisterV1RestSharpClient(IServiceCollection services, bool throwOnError)
{
+#if NET6_0_OR_GREATER
services.AddSingleton(provider =>
{
var settings = provider.GetRequiredService>().Value;
@@ -131,6 +159,38 @@ private static void RegisterV1RestSharpClient(IServiceCollection services, bool
};
return new RestClient(clientOptions);
});
+#else
+ services.AddSingleton(provider =>
+ {
+ var settings = provider.GetRequiredService>().Value;
+ settings.MindeeBaseUrl = Environment.GetEnvironmentVariable("Mindee__BaseUrl");
+ if (string.IsNullOrEmpty(settings.MindeeBaseUrl))
+ {
+ settings.MindeeBaseUrl = "https://api.mindee.net";
+ }
+
+ if (settings.RequestTimeoutSeconds <= 0)
+ {
+ settings.RequestTimeoutSeconds = 120;
+ }
+
+ if (string.IsNullOrEmpty(settings.ApiKey))
+ {
+ settings.ApiKey = Environment.GetEnvironmentVariable("Mindee__ApiKey");
+ }
+
+ var clientOptions = new RestClientOptions(settings.MindeeBaseUrl)
+ {
+ FollowRedirects = false,
+ Timeout = TimeSpan.FromSeconds(settings.RequestTimeoutSeconds),
+ UserAgent = BuildUserAgent(),
+ Expect100Continue = false,
+ CachePolicy = new CacheControlHeaderValue { NoCache = true, NoStore = true },
+ ThrowOnAnyError = throwOnError
+ };
+ return new MindeeV1RestClientWrapper(new RestClient(clientOptions));
+ });
+#endif
}
private static void RegisterV2RestSharpClient(IServiceCollection services, bool throwOnError)
@@ -240,7 +300,17 @@ public static IServiceCollection AddMindeeClient(
string sectionName = "Mindee")
{
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton(provider =>
+ {
+ var settings = provider.GetRequiredService>();
+#if NET6_0_OR_GREATER
+ var restClient = provider.GetRequiredService();
+#else
+ var restClient = provider.GetRequiredService().Client;
+#endif
+ var logger = provider.GetService()?.CreateLogger();
+ return new MindeeApi(settings, restClient, logger);
+ });
services.AddOptions()
.BindConfiguration(sectionName)
.Validate(settings => !string.IsNullOrEmpty(settings.ApiKey), "The Mindee V1 API key is missing");
@@ -264,7 +334,17 @@ public static IServiceCollection AddMindeeClientV2(
string sectionName = "MindeeV2")
{
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton(provider =>
+ {
+ var settings = provider.GetRequiredService>();
+#if NET6_0_OR_GREATER
+ var restClient = provider.GetRequiredKeyedService("MindeeV2RestClient");
+#else
+ var restClient = provider.GetRequiredService().Client;
+#endif
+ var logger = provider.GetService()?.CreateLogger();
+ return new MindeeApiV2(settings, restClient, logger);
+ });
services.AddOptions()
.BindConfiguration(sectionName)
.Validate(settings => !string.IsNullOrEmpty(settings.ApiKey), "The Mindee V2 API key is missing");
diff --git a/src/Mindee/Image/ImageCompressor.cs b/src/Mindee/Image/ImageCompressor.cs
index 561782fc..414c8cc1 100644
--- a/src/Mindee/Image/ImageCompressor.cs
+++ b/src/Mindee/Image/ImageCompressor.cs
@@ -19,8 +19,9 @@ public static byte[] CompressImage(SKBitmap original, int quality, int finalWidt
{
using var image = SKImage.FromBitmap(original);
using var compressedBitmap = SKBitmap.FromImage(image);
+ var samplingOptions = new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.None);
using var finalImage =
- compressedBitmap.Resize(new SKImageInfo(finalWidth, finalHeight), SKFilterQuality.Low);
+ compressedBitmap.Resize(new SKImageInfo(finalWidth, finalHeight), samplingOptions);
return finalImage.Encode(SKEncodedImageFormat.Jpeg, quality).ToArray();
}
diff --git a/src/Mindee/Mindee.csproj b/src/Mindee/Mindee.csproj
index 50e08c21..44272c73 100644
--- a/src/Mindee/Mindee.csproj
+++ b/src/Mindee/Mindee.csproj
@@ -4,40 +4,13 @@
net472;net48;net6.0;net7.0;net8.0;net9.0;net10.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<_Parameter1>Mindee.UnitTests
+
+ <_Parameter1>Mindee.IntegrationTests
+
diff --git a/src/Mindee/Pdf/PdfCompressor.cs b/src/Mindee/Pdf/PdfCompressor.cs
index 084352a5..ff9a69de 100644
--- a/src/Mindee/Pdf/PdfCompressor.cs
+++ b/src/Mindee/Pdf/PdfCompressor.cs
@@ -45,7 +45,12 @@ public static byte[] CompressPdf(
using var docReader = DocLib.Instance.GetDocReader(pdfData, new PageDimensions(1));
var outputStream = new MemoryStream();
- using (var document = SKDocument.CreatePdf(outputStream))
+ var metadata = new SKDocumentPdfMetadata
+ {
+ EncodingQuality = imageQuality
+ };
+
+ using (var document = SKDocument.CreatePdf(outputStream, metadata))
{
ProcessPages(docReader, document, imageQuality, disableSourceText);
}
diff --git a/src/Mindee/Pdf/PdfUtils.cs b/src/Mindee/Pdf/PdfUtils.cs
index 7cf3c2f3..1aff8156 100644
--- a/src/Mindee/Pdf/PdfUtils.cs
+++ b/src/Mindee/Pdf/PdfUtils.cs
@@ -60,12 +60,8 @@ public static SKBitmap GeneratePageBitmap(int imageQuality, IPageReader pageRead
var compressedImage = ImageCompressor.CompressImage(initialBitmap, imageQuality, width, height);
- var colorType = SKColorType.Argb4444;
using var compressedBitmap = SKBitmap.Decode(compressedImage);
- if (imageQuality > 85)
- {
- colorType = SKColorType.Rgb565;
- }
+ const SKColorType colorType = SKColorType.Rgb565;
using var surface = SKSurface.Create(new SKImageInfo(width, height, colorType));
@@ -93,8 +89,9 @@ public static SKBitmap GeneratePageBitmap(int imageQuality, IPageReader pageRead
private static void WriteTextToCanvas(SKBitmap bitmap, Character character, SKCanvas canvas)
{
using var paint = new SKPaint();
+ using var font = new SKFont();
var textColor = ImageUtils.InferTextColor(bitmap, character.Box);
- paint.TextSize = (float)character.FontSize * (72f / 96f);
+ font.Size = (float)character.FontSize * (72f / 96f);
paint.Color = textColor;
var fontManager = SKFontManager.Default;
@@ -105,14 +102,12 @@ private static void WriteTextToCanvas(SKBitmap bitmap, Character character, SKCa
string.Equals(fontManager.MatchFamily(tmpFontName).FamilyName, tmpFontName,
StringComparison.OrdinalIgnoreCase)
) ?? "Liberation Sans";
- paint.Typeface = SKTypeface.FromFamilyName(fontName);
-
- paint.TextAlign = SKTextAlign.Left;
+ font.Typeface = SKTypeface.FromFamilyName(fontName);
var text = character.Char.ToString();
var lines = text.Split(["\r\n", "\n"], StringSplitOptions.None);
- var lineHeight = paint.FontSpacing;
+ var lineHeight = font.Spacing;
var boxCenterX = (character.Box.Left + character.Box.Right) / 2f;
float boxBottom = character.Box.Bottom;
@@ -124,8 +119,7 @@ private static void WriteTextToCanvas(SKBitmap bitmap, Character character, SKCa
continue;
}
- var lineBounds = new SKRect();
- paint.MeasureText(line, ref lineBounds);
+ font.MeasureText(line, out var lineBounds);
var x = boxCenterX - (lineBounds.Width / 2f);
var y = boxBottom - ((lines.Length - i) * lineHeight);
@@ -139,8 +133,8 @@ private static void WriteTextToCanvas(SKBitmap bitmap, Character character, SKCa
}
var charString = c.ToString();
- canvas.DrawText(charString, x, y, paint);
- x += paint.MeasureText(charString);
+ canvas.DrawText(charString, x, y, font, paint);
+ x += font.MeasureText(charString);
}
}
}
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
new file mode 100644
index 00000000..25c2506d
--- /dev/null
+++ b/tests/Directory.Build.props
@@ -0,0 +1,40 @@
+
+
+
+ latest
+ false
+ true
+ false
+ enable
+ $(NoWarn);1591
+ false
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+ PreserveNewest
+ Resources\%(RecursiveDir)%(Filename)%(Extension)
+
+
+
+
+ Resources\async
+
+
+
+
diff --git a/tests/Mindee.IntegrationTests/Mindee.IntegrationTests.csproj b/tests/Mindee.IntegrationTests/Mindee.IntegrationTests.csproj
index 1cd32e3f..037b40f1 100644
--- a/tests/Mindee.IntegrationTests/Mindee.IntegrationTests.csproj
+++ b/tests/Mindee.IntegrationTests/Mindee.IntegrationTests.csproj
@@ -1,49 +1,18 @@
-
net472;net48;net6.0;net7.0;net8.0;net9.0;net10.0
enable
- enable
- $(NoWarn);1591
- false
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
+
-
-
-
-
-
+
+
-
-
-
-
- PreserveNewest
-
-
-
-
-
-
-
diff --git a/tests/Mindee.UnitTests/Mindee.UnitTests.csproj b/tests/Mindee.UnitTests/Mindee.UnitTests.csproj
index 034220a4..1c1850f8 100644
--- a/tests/Mindee.UnitTests/Mindee.UnitTests.csproj
+++ b/tests/Mindee.UnitTests/Mindee.UnitTests.csproj
@@ -1,52 +1,13 @@
-
net472;net48;net6.0;net7.0;net8.0;net9.0;net10.0
enable
- enable
- $(NoWarn);1591
- false
-
-
-
-
-
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
+
-
-
-
- Resources\async
-
-
-
-
diff --git a/tests/Mindee.UnitTests/UnitTestBase.cs b/tests/Mindee.UnitTests/UnitTestBase.cs
index fb6ddaa3..89661952 100644
--- a/tests/Mindee.UnitTests/UnitTestBase.cs
+++ b/tests/Mindee.UnitTests/UnitTestBase.cs
@@ -59,11 +59,16 @@ public static ServiceProvider InitServiceProviderV1(HttpStatusCode statusCode, s
options.RequestTimeoutSeconds = 120;
});
- services.AddSingleton(new RestClient(new RestClientOptions
+ var restClient = new RestClient(new RestClientOptions
{
BaseUrl = new Uri("https://api.mindee.net"),
ConfigureMessageHandler = _ => mockHttpMessageHandler.Object
- }));
+ });
+#if NET6_0_OR_GREATER
+ services.AddSingleton(restClient);
+#else
+ services.AddSingleton(new MindeeV1RestClientWrapper(restClient));
+#endif
return services.BuildServiceProvider();
}
@@ -98,12 +103,16 @@ private static ServiceProvider InitServiceProviderV2(HttpStatusCode statusCode,
options.RequestTimeoutSeconds = 120;
});
- services.AddKeyedSingleton("MindeeV2RestClient",
- new RestClient(new RestClientOptions
- {
- BaseUrl = new Uri("https://api.mindee.net"),
- ConfigureMessageHandler = _ => mockHttpMessageHandler.Object
- }));
+ var restClient = new RestClient(new RestClientOptions
+ {
+ BaseUrl = new Uri("https://api.mindee.net"),
+ ConfigureMessageHandler = _ => mockHttpMessageHandler.Object
+ });
+#if NET6_0_OR_GREATER
+ services.AddKeyedSingleton("MindeeV2RestClient", restClient);
+#else
+ services.AddSingleton(new MindeeV2RestClientWrapper(restClient));
+#endif
return services.BuildServiceProvider();
}