mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
Remove GUI-related dependencies from NAPS2.Sdk and have unit tests specify all their own paths (rather than using the Paths constants that reference the real user appdata etc)
This commit is contained in:
parent
2f2211e1fc
commit
ba16ac845e
@ -19,7 +19,7 @@ public static class ServerEntryPoint
|
||||
try
|
||||
{
|
||||
// Initialize Ninject (the DI framework)
|
||||
var kernel = new StandardKernel(new CommonModule(), new WinFormsModule(), new StaticDefaultsModule(), new RecoveryModule());
|
||||
var kernel = new StandardKernel(new CommonModule(), new WinFormsModule(), new PathsModule(), new RecoveryModule());
|
||||
|
||||
// Start a pending worker process
|
||||
kernel.Get<IWorkerFactory>().Init();
|
||||
|
@ -1,14 +1,16 @@
|
||||
using Eto.Drawing;
|
||||
|
||||
namespace NAPS2.Config;
|
||||
|
||||
public class FormState
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
|
||||
public Point Location { get; set; }
|
||||
public FormLocation Location { get; set; }
|
||||
|
||||
public Size Size { get; set; }
|
||||
public FormSize Size { get; set; }
|
||||
|
||||
public bool Maximized { get; set; }
|
||||
|
||||
public record FormLocation(int X, int Y);
|
||||
|
||||
public record FormSize(int Width, int Height);
|
||||
}
|
@ -43,12 +43,14 @@ public class ProfileManager : IProfileManager
|
||||
|
||||
public void Mutate(ListMutation<ScanProfile> mutation, ISelectable<ScanProfile> selectable)
|
||||
{
|
||||
Load();
|
||||
mutation.Apply(_profiles, selectable);
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Mutate(ListMutation<ScanProfile> mutation, ListSelection<ScanProfile> selection)
|
||||
{
|
||||
Load();
|
||||
mutation.Apply(_profiles, ref selection);
|
||||
Save();
|
||||
}
|
||||
@ -77,8 +79,8 @@ public class ProfileManager : IProfileManager
|
||||
profile.IsDefault = false;
|
||||
}
|
||||
value.IsDefault = true;
|
||||
Save();
|
||||
}
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ public class SaveImagesOperation : OperationBase
|
||||
{
|
||||
if (File.Exists(subFileName))
|
||||
{
|
||||
if (_overwritePrompt.ConfirmOverwrite(subFileName) != DialogResult.Yes)
|
||||
if (_overwritePrompt.ConfirmOverwrite(subFileName) != OverwriteResponse.Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -81,12 +81,12 @@ public class SaveImagesOperation : OperationBase
|
||||
|
||||
if (images.Count == 1 && File.Exists(subFileName))
|
||||
{
|
||||
var dialogResult = _overwritePrompt.ConfirmOverwrite(subFileName);
|
||||
if (dialogResult == DialogResult.No)
|
||||
var overwriteResponse = _overwritePrompt.ConfirmOverwrite(subFileName);
|
||||
if (overwriteResponse == OverwriteResponse.No)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (dialogResult == DialogResult.Cancel)
|
||||
if (overwriteResponse == OverwriteResponse.Abort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public class SavePdfOperation : OperationBase
|
||||
}
|
||||
if (File.Exists(subFileName))
|
||||
{
|
||||
if (_overwritePrompt.ConfirmOverwrite(subFileName) != DialogResult.Yes)
|
||||
if (_overwritePrompt.ConfirmOverwrite(subFileName) != OverwriteResponse.Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ public class CommonModule : NinjectModule
|
||||
|
||||
// Export
|
||||
Bind<PdfExporter>().To<PdfSharpExporter>();
|
||||
Bind<IScannedImagePrinter>().To<PrintDocumentPrinter>();
|
||||
Bind<IEmailProviderFactory>().To<NinjectEmailProviderFactory>();
|
||||
Bind<IMapiWrapper>().To<MapiWrapper>();
|
||||
Bind<OcrRequestQueue>().ToSelf().InSingletonScope();
|
||||
@ -46,11 +45,14 @@ public class CommonModule : NinjectModule
|
||||
Bind<NetworkScanBridge>().ToSelf();
|
||||
|
||||
// Config
|
||||
var config = new ScopedConfig(Path.Combine(Paths.Executable, "appsettings.xml"), Path.Combine(Paths.AppData, "config.xml"));
|
||||
Bind<ScopedConfig>().ToConstant(config);
|
||||
Bind<ScopedConfig>().ToMethod(_ =>
|
||||
new ScopedConfig(Path.Combine(Paths.Executable, "appsettings.xml"),
|
||||
Path.Combine(Paths.AppData, "config.xml"))).InSingletonScope();
|
||||
Bind<IConfigProvider<PdfSettings>>().ToMethod(ctx => ctx.Kernel.Get<ScopedConfig>().Child(c => c.PdfSettings));
|
||||
Bind<IConfigProvider<ImageSettings>>().ToMethod(ctx => ctx.Kernel.Get<ScopedConfig>().Child(c => c.ImageSettings));
|
||||
Bind<IConfigProvider<EmailSettings>>().ToMethod(ctx => ctx.Kernel.Get<ScopedConfig>().Child(c => c.EmailSettings));
|
||||
Bind<IConfigProvider<ImageSettings>>()
|
||||
.ToMethod(ctx => ctx.Kernel.Get<ScopedConfig>().Child(c => c.ImageSettings));
|
||||
Bind<IConfigProvider<EmailSettings>>()
|
||||
.ToMethod(ctx => ctx.Kernel.Get<ScopedConfig>().Child(c => c.EmailSettings));
|
||||
Bind<IConfigProvider<EmailSetup>>().ToMethod(ctx => ctx.Kernel.Get<ScopedConfig>().Child(c => c.EmailSetup));
|
||||
|
||||
// Host
|
||||
@ -68,25 +70,35 @@ public class CommonModule : NinjectModule
|
||||
|
||||
//Kernel.Get<ImageContext>().PdfRenderer = Kernel.Get<PdfiumWorkerCoordinator>();
|
||||
|
||||
var profileManager = new ProfileManager(
|
||||
Bind<IProfileManager>().ToMethod(ctx =>
|
||||
{
|
||||
var config = ctx.Kernel.Get<ScopedConfig>();
|
||||
return new ProfileManager(
|
||||
Path.Combine(Paths.AppData, "profiles.xml"),
|
||||
Path.Combine(Paths.Executable, "profiles.xml"),
|
||||
Path.Combine(Assembly.GetEntryAssembly().Location, "profiles.xml"),
|
||||
config.Get(c => c.LockSystemProfiles),
|
||||
config.Get(c => c.LockUnspecifiedDevices),
|
||||
config.Get(c => c.NoUserProfiles));
|
||||
Bind<IProfileManager>().ToConstant(profileManager);
|
||||
}).InSingletonScope();
|
||||
|
||||
Bind<TesseractLanguageManager>().ToMethod(ctx =>
|
||||
{
|
||||
var config = ctx.Kernel.Get<ScopedConfig>();
|
||||
var customComponentsPath = config.Get(c => c.ComponentsPath);
|
||||
var componentsPath = string.IsNullOrWhiteSpace(customComponentsPath)
|
||||
? Paths.Components
|
||||
: Environment.ExpandEnvironmentVariables(customComponentsPath);
|
||||
var tesseractLanguageManager = new TesseractLanguageManager(componentsPath);
|
||||
var naps2Folder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
// TODO: Linux/mac. Also maybe generalize this (see also PdfiumNativeLibrary).
|
||||
var tesseractPath =
|
||||
Path.Combine(naps2Folder!, Environment.Is64BitProcess ? "_win64" : "_win32", "tesseract.exe");
|
||||
var tesseractOcrEngine = new TesseractOcrEngine(tesseractPath, tesseractLanguageManager.TessdataBasePath);
|
||||
Bind<TesseractLanguageManager>().ToConstant(tesseractLanguageManager);
|
||||
Bind<IOcrEngine>().ToConstant(tesseractOcrEngine);
|
||||
return new TesseractLanguageManager(componentsPath);
|
||||
}).InSingletonScope();
|
||||
Bind<IOcrEngine>().ToMethod(ctx =>
|
||||
{
|
||||
var tesseractPath = PlatformCompat.System.UseSystemTesseract
|
||||
? "tesseract"
|
||||
: Path.Combine(Paths.Executable, PlatformCompat.System.TesseractExecutablePath!);
|
||||
return new TesseractOcrEngine(
|
||||
tesseractPath,
|
||||
ctx.Kernel.Get<TesseractLanguageManager>().TessdataBasePath,
|
||||
Paths.Temp);
|
||||
}).InSingletonScope();
|
||||
}
|
||||
}
|
13
NAPS2.Lib.Common/Modules/PathsModule.cs
Normal file
13
NAPS2.Lib.Common/Modules/PathsModule.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using NAPS2.Scan;
|
||||
using Ninject;
|
||||
using Ninject.Modules;
|
||||
|
||||
namespace NAPS2.Modules;
|
||||
|
||||
public class PathsModule : NinjectModule
|
||||
{
|
||||
public override void Load()
|
||||
{
|
||||
Kernel.Get<ScanningContext>().TempFolderPath = Paths.Temp;
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
using Ninject;
|
||||
using Ninject.Modules;
|
||||
|
||||
namespace NAPS2.Modules;
|
||||
|
||||
public class StaticDefaultsModule : NinjectModule
|
||||
{
|
||||
public override void Load()
|
||||
{
|
||||
OperationProgress.Default = Kernel.Get<OperationProgress>();
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using System.Windows.Forms;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NAPS2;
|
||||
|
||||
public static class Paths
|
||||
{
|
||||
private static readonly string ExecutablePath = Application.StartupPath;
|
||||
private static readonly string ExecutablePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
|
||||
#if STANDALONE
|
||||
private static readonly string AppDataPath = Path.Combine(ExecutablePath, "..", "Data");
|
5
NAPS2.Lib.Common/Util/IsExternalInit.cs
Normal file
5
NAPS2.Lib.Common/Util/IsExternalInit.cs
Normal file
@ -0,0 +1,5 @@
|
||||
// https://sergiopedri.medium.com/enabling-and-using-c-9-features-on-older-and-unsupported-runtimes-ce384d8debb
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Runtime.CompilerServices;
|
||||
|
||||
internal static class IsExternalInit {}
|
@ -14,16 +14,16 @@ public class ConsoleOverwritePrompt : OverwritePrompt
|
||||
_errorOutput = errorOutput;
|
||||
}
|
||||
|
||||
public override DialogResult ConfirmOverwrite(string path)
|
||||
public override OverwriteResponse ConfirmOverwrite(string path)
|
||||
{
|
||||
if (ForceOverwrite)
|
||||
{
|
||||
return DialogResult.Yes;
|
||||
return OverwriteResponse.Yes;
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorOutput.DisplayError(string.Format(ConsoleResources.FileAlreadyExists, path));
|
||||
return DialogResult.No;
|
||||
return OverwriteResponse.No;
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ public static class ConsoleEntryPoint
|
||||
public static void Run(string[] args)
|
||||
{
|
||||
// Initialize Ninject (the DI framework)
|
||||
var kernel = new StandardKernel(new CommonModule(), new ConsoleModule(), new StaticDefaultsModule(), new RecoveryModule());
|
||||
var kernel = new StandardKernel(new CommonModule(), new ConsoleModule(), new PathsModule(), new RecoveryModule());
|
||||
|
||||
Paths.ClearTemp();
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
<!-- TODO: Do we want to remove this reference? -->
|
||||
<ProjectReference Include="..\NAPS2.Lib.WinForms\NAPS2.Lib.WinForms.csproj" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<PackageReference Include="CommandLineParser" Version="1.9.71" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,13 +1,16 @@
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using NAPS2.Automation;
|
||||
using NAPS2.Modules;
|
||||
using NAPS2.Ocr;
|
||||
using NAPS2.Scan;
|
||||
using NAPS2.Scan.Internal;
|
||||
using NAPS2.Sdk.Tests;
|
||||
using NAPS2.Sdk.Tests.Asserts;
|
||||
using NAPS2.Sdk.Tests.Images;
|
||||
using NAPS2.Sdk.Tests.Mocks;
|
||||
using NAPS2.Sdk.Tests.Ocr;
|
||||
using Ninject;
|
||||
using Ninject.Modules;
|
||||
using Ninject.Parameters;
|
||||
@ -28,7 +31,8 @@ public class CommandLineIntegrationTests : ContextualTexts
|
||||
private async Task RunCommand(AutomatedScanningOptions options, params Bitmap[] imagesToScan)
|
||||
{
|
||||
var scanDriverFactory = new ScanDriverFactoryBuilder().WithScannedImages(imagesToScan).Build();
|
||||
var kernel = new StandardKernel(new CommonModule(), new ConsoleModule(), new TestModule(ImageContext, scanDriverFactory, _testOutputHelper, FolderPath));
|
||||
var kernel = new StandardKernel(new CommonModule(), new ConsoleModule(),
|
||||
new TestModule(ScanningContext, ImageContext, scanDriverFactory, _testOutputHelper, FolderPath));
|
||||
var automatedScanning = kernel.Get<AutomatedScanning>(new ConstructorArgument("options", options));
|
||||
await automatedScanning.Execute();
|
||||
}
|
||||
@ -36,15 +40,17 @@ public class CommandLineIntegrationTests : ContextualTexts
|
||||
[Fact]
|
||||
public async Task ScanSanity()
|
||||
{
|
||||
var path = $"{FolderPath}/test.pdf";
|
||||
await RunCommand(
|
||||
new AutomatedScanningOptions
|
||||
{
|
||||
Number = 1,
|
||||
OutputPath = $"{FolderPath}/test.pdf",
|
||||
OutputPath = path,
|
||||
Verbose = true
|
||||
},
|
||||
SharedData.color_image);
|
||||
PdfAsserts.AssertPageCount(1, $"{FolderPath}/test.pdf");
|
||||
Assert.True(File.Exists(path));
|
||||
PdfAsserts.AssertPageCount(1, path);
|
||||
AssertRecoveryCleanedUp();
|
||||
}
|
||||
|
||||
@ -77,16 +83,23 @@ public class CommandLineIntegrationTests : ContextualTexts
|
||||
[Fact]
|
||||
public async Task ScanWithOcr()
|
||||
{
|
||||
var fast = Path.Combine(FolderPath, "fast");
|
||||
Directory.CreateDirectory(fast);
|
||||
CopyResourceToFile(TesseractResources.tesseract_x64, FolderPath, "tesseract.exe");
|
||||
CopyResourceToFile(TesseractResources.eng_traineddata, fast, "eng.traineddata");
|
||||
|
||||
var path = $"{FolderPath}/test.pdf";
|
||||
await RunCommand(
|
||||
new AutomatedScanningOptions
|
||||
{
|
||||
Number = 1,
|
||||
OutputPath = $"{FolderPath}/test.pdf",
|
||||
OutputPath = path,
|
||||
Verbose = true,
|
||||
OcrLang = "eng"
|
||||
},
|
||||
SharedData.ocr_test);
|
||||
PdfAsserts.AssertContainsText("ADVERTISEMENT.", $"{FolderPath}/test.pdf");
|
||||
Assert.True(File.Exists(path));
|
||||
PdfAsserts.AssertContainsText("ADVERTISEMENT.", path);
|
||||
AssertRecoveryCleanedUp();
|
||||
}
|
||||
|
||||
@ -99,14 +112,17 @@ public class CommandLineIntegrationTests : ContextualTexts
|
||||
|
||||
private class TestModule : NinjectModule
|
||||
{
|
||||
private readonly ScanningContext _scanningContext;
|
||||
private readonly ImageContext _imageContext;
|
||||
private readonly IScanDriverFactory _scanDriverFactory;
|
||||
private readonly ITestOutputHelper _testOutputHelper;
|
||||
private readonly string _folderPath;
|
||||
|
||||
public TestModule(ImageContext imageContext, IScanDriverFactory scanDriverFactory,
|
||||
public TestModule(ScanningContext scanningContext, ImageContext imageContext,
|
||||
IScanDriverFactory scanDriverFactory,
|
||||
ITestOutputHelper testOutputHelper, string folderPath)
|
||||
{
|
||||
_scanningContext = scanningContext;
|
||||
_imageContext = imageContext;
|
||||
_scanDriverFactory = scanDriverFactory;
|
||||
_testOutputHelper = testOutputHelper;
|
||||
@ -116,16 +132,49 @@ public class CommandLineIntegrationTests : ContextualTexts
|
||||
public override void Load()
|
||||
{
|
||||
Rebind<ImageContext>().ToConstant(_imageContext);
|
||||
// TODO: Bind TesseractLanguageManager or at least the language data path
|
||||
Rebind<IScanDriverFactory>().ToConstant(_scanDriverFactory);
|
||||
Rebind<IScanBridgeFactory>().To<InProcScanBridgeFactory>();
|
||||
Rebind<ConsoleOutput>().ToSelf().WithConstructorArgument("writer", new TestOutputTextWriter(_testOutputHelper));
|
||||
Rebind<ConsoleOutput>().ToSelf()
|
||||
.WithConstructorArgument("writer", new TestOutputTextWriter(_testOutputHelper));
|
||||
Rebind<ScopedConfig>().ToMethod(_ =>
|
||||
{
|
||||
var appConfigPath = Path.Combine(_folderPath, "appsettings.xml");
|
||||
var userConfigPath = Path.Combine(_folderPath, "config.xml");
|
||||
return new ScopedConfig(appConfigPath, userConfigPath);
|
||||
}).InSingletonScope();
|
||||
Rebind<IProfileManager>().ToMethod(_ =>
|
||||
{
|
||||
var userPath = Path.Combine(_folderPath, "profiles.xml");
|
||||
var systemPath = Path.Combine(_folderPath, "sysprofiles.xml");
|
||||
var profileManager = new ProfileManager(userPath, systemPath, false, false, false);
|
||||
var defaultProfile = new ScanProfile
|
||||
{
|
||||
IsDefault = true,
|
||||
Device = new ScanDevice("001", "Some Scanner")
|
||||
};
|
||||
profileManager.Mutate(
|
||||
new ListMutation<ScanProfile>.Append(defaultProfile),
|
||||
new Selectable<ScanProfile>());
|
||||
return profileManager;
|
||||
}).InSingletonScope();
|
||||
Rebind<TesseractLanguageManager>().ToMethod(_ =>
|
||||
{
|
||||
var componentsPath = Path.Combine(_folderPath, "components");
|
||||
Directory.CreateDirectory(componentsPath);
|
||||
return new TesseractLanguageManager(componentsPath);
|
||||
}).InSingletonScope();
|
||||
Rebind<IOcrEngine>().ToMethod(ctx => new TesseractOcrEngine(
|
||||
Path.Combine(_folderPath, "tesseract.exe"),
|
||||
_folderPath,
|
||||
_folderPath)).InSingletonScope();
|
||||
|
||||
string recoveryFolderPath = Path.Combine(_folderPath, "recovery");
|
||||
var recoveryStorageManager = RecoveryStorageManager.CreateFolder(recoveryFolderPath);
|
||||
var fileStorageManager = new FileStorageManager(recoveryFolderPath);
|
||||
Kernel.Bind<RecoveryStorageManager>().ToConstant(recoveryStorageManager);
|
||||
Kernel.Bind<FileStorageManager>().ToConstant(fileStorageManager);
|
||||
|
||||
Kernel.Get<ScanningContext>().TempFolderPath = _scanningContext.TempFolderPath;
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,6 +186,7 @@ public class CommandLineIntegrationTests : ContextualTexts
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
|
||||
public override void WriteLine(string message) => _output.WriteLine(message);
|
||||
|
@ -16,7 +16,7 @@ public static class WinFormsEntryPoint
|
||||
public static void Run(string[] args)
|
||||
{
|
||||
// Initialize Ninject (the DI framework)
|
||||
var kernel = new StandardKernel(new CommonModule(), new WinFormsModule(), new StaticDefaultsModule(), new RecoveryModule());
|
||||
var kernel = new StandardKernel(new CommonModule(), new WinFormsModule(), new PathsModule(), new RecoveryModule());
|
||||
|
||||
Paths.ClearTemp();
|
||||
|
||||
|
@ -25,7 +25,7 @@ public static class WorkerEntryPoint
|
||||
#endif
|
||||
|
||||
// Initialize Ninject (the DI framework)
|
||||
var kernel = new StandardKernel(new CommonModule(), new WinFormsModule(), new StaticDefaultsModule());
|
||||
var kernel = new StandardKernel(new CommonModule(), new WinFormsModule(), new PathsModule());
|
||||
// Verify that the recovery is always initialized by the parent process before creating images
|
||||
// TODO: Replace this with something maybe
|
||||
// kernel.Get<ImageContext>().UseFileStorage(new NotToBeUsedStorageManager());
|
||||
|
@ -1,3 +1,4 @@
|
||||
using Eto.Drawing;
|
||||
using Eto.Forms;
|
||||
|
||||
namespace NAPS2.EtoForms;
|
||||
@ -46,18 +47,20 @@ public class FormStateController : IFormStateController
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
if (!_formState.Location.IsZero)
|
||||
var location = new Point(_formState.Location.X, _formState.Location.Y);
|
||||
var size = new Size(_formState.Size.Width, _formState.Size.Height);
|
||||
if (!location.IsZero)
|
||||
{
|
||||
if (Screen.Screens.Any(x => x.WorkingArea.Contains(_formState.Location)))
|
||||
if (Screen.Screens.Any(x => x.WorkingArea.Contains(location)))
|
||||
{
|
||||
// Only move to the specified location if it's onscreen
|
||||
// It might be offscreen if the user has disconnected a monitor
|
||||
_window.Location = _formState.Location;
|
||||
_window.Location = location;
|
||||
}
|
||||
}
|
||||
if (!_formState.Size.IsEmpty)
|
||||
if (!size.IsEmpty)
|
||||
{
|
||||
_window.Size = _formState.Size;
|
||||
_window.Size = size;
|
||||
}
|
||||
if (_formState.Maximized)
|
||||
{
|
||||
@ -72,7 +75,7 @@ public class FormStateController : IFormStateController
|
||||
_formState.Maximized = (_window.WindowState == WindowState.Maximized);
|
||||
if (_window.WindowState == WindowState.Normal)
|
||||
{
|
||||
_formState.Size = _window.Size;
|
||||
_formState.Size = new FormState.FormSize(_window.Size.Width, _window.Size.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,7 +86,7 @@ public class FormStateController : IFormStateController
|
||||
{
|
||||
if (_window.WindowState == WindowState.Normal)
|
||||
{
|
||||
_formState.Location = _window.Location;
|
||||
_formState.Location = new FormState.FormLocation(_window.Location.X, _window.Location.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using NAPS2.Dependencies;
|
||||
using NAPS2.EtoForms;
|
||||
using NAPS2.EtoForms.WinForms;
|
||||
using NAPS2.ImportExport;
|
||||
using NAPS2.ImportExport.Pdf;
|
||||
using NAPS2.Scan.Batch;
|
||||
using NAPS2.WinForms;
|
||||
@ -23,5 +24,6 @@ public class WinFormsModule : NinjectModule
|
||||
Bind<IEtoPlatform>().To<WinFormsEtoPlatform>();
|
||||
Bind<NotificationManager>().ToSelf().InSingletonScope();
|
||||
Bind<ISaveNotify>().ToMethod(ctx => ctx.Kernel.Get<NotificationManager>());
|
||||
Bind<IScannedImagePrinter>().To<PrintDocumentPrinter>();
|
||||
}
|
||||
}
|
@ -19,6 +19,8 @@
|
||||
</Reference>
|
||||
<ProjectReference Include="..\NAPS2.Lib.Common\NAPS2.Lib.Common.csproj" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<PackageReference Include="Eto.Forms" Version="2.5.10" />
|
||||
<PackageReference Include="Eto.Platform.Windows" Version="2.5.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Globalization;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Windows.Forms;
|
||||
using Eto;
|
||||
using NAPS2.EtoForms;
|
||||
@ -145,18 +146,20 @@ public class FormBase : Form, IInvoker, IFormBase
|
||||
|
||||
protected void DoRestoreFormState()
|
||||
{
|
||||
if (!_formState.Location.IsZero)
|
||||
var location = new Point(_formState.Location.X, _formState.Location.Y);
|
||||
var size = new Size(_formState.Size.Width, _formState.Size.Height);
|
||||
if (!location.IsEmpty)
|
||||
{
|
||||
if (Screen.AllScreens.Any(x => x.WorkingArea.Contains(_formState.Location.ToSD())))
|
||||
if (Screen.AllScreens.Any(x => x.WorkingArea.Contains(location)))
|
||||
{
|
||||
// Only move to the specified location if it's onscreen
|
||||
// It might be offscreen if the user has disconnected a monitor
|
||||
Location = _formState.Location.ToSD();
|
||||
Location = location;
|
||||
}
|
||||
}
|
||||
if (!_formState.Size.IsEmpty)
|
||||
if (!size.IsEmpty)
|
||||
{
|
||||
Size = _formState.Size.ToSD();
|
||||
Size = size;
|
||||
}
|
||||
if (_formState.Maximized)
|
||||
{
|
||||
@ -171,7 +174,7 @@ public class FormBase : Form, IInvoker, IFormBase
|
||||
_formState.Maximized = (WindowState == FormWindowState.Maximized);
|
||||
if (WindowState == FormWindowState.Normal)
|
||||
{
|
||||
_formState.Size = Size.ToEto();
|
||||
_formState.Size = new FormState.FormSize(Size.Width, Size.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,7 +185,7 @@ public class FormBase : Form, IInvoker, IFormBase
|
||||
{
|
||||
if (WindowState == FormWindowState.Normal)
|
||||
{
|
||||
_formState.Location = Location.ToEto();
|
||||
_formState.Location = new FormState.FormLocation(Location.X, Location.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,16 @@ namespace NAPS2.WinForms;
|
||||
|
||||
public class WinFormsOverwritePrompt : OverwritePrompt
|
||||
{
|
||||
public override DialogResult ConfirmOverwrite(string path)
|
||||
public override OverwriteResponse ConfirmOverwrite(string path)
|
||||
{
|
||||
string fileName = Path.GetFileName(path);
|
||||
var dialogResult = MessageBox.Show(string.Format(MiscResources.ConfirmOverwriteFile, fileName),
|
||||
MiscResources.OverwriteFile, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning);
|
||||
return dialogResult;
|
||||
return dialogResult switch
|
||||
{
|
||||
DialogResult.Yes => OverwriteResponse.Yes,
|
||||
DialogResult.No => OverwriteResponse.No,
|
||||
_ => OverwriteResponse.Abort
|
||||
};
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ public class ContextualTexts : IDisposable
|
||||
|
||||
ImageContext = new GdiImageContext();
|
||||
ScanningContext = new ScanningContext(ImageContext);
|
||||
ScanningContext.TempFolderPath = Path.Combine(FolderPath, "temp");
|
||||
Directory.CreateDirectory(ScanningContext.TempFolderPath);
|
||||
}
|
||||
|
||||
public ImageContext ImageContext { get; }
|
||||
@ -29,6 +31,18 @@ public class ContextualTexts : IDisposable
|
||||
return ScanningContext.CreateProcessedImage(new GdiImage(new Bitmap(100, 100)));
|
||||
}
|
||||
|
||||
public string CopyResourceToFile(byte[] resource, string folder, string fileName)
|
||||
{
|
||||
string path = Path.Combine(folder, fileName);
|
||||
File.WriteAllBytes(path, resource);
|
||||
return path;
|
||||
}
|
||||
|
||||
public string CopyResourceToFile(byte[] resource, string fileName)
|
||||
{
|
||||
return CopyResourceToFile(resource, FolderPath, fileName);
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
ScanningContext.Dispose();
|
||||
@ -39,7 +53,13 @@ public class ContextualTexts : IDisposable
|
||||
catch (IOException)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
try
|
||||
{
|
||||
Directory.Delete(FolderPath, true);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,25 +17,13 @@ public class TesseractOcrEngineTests : ContextualTexts
|
||||
var fast = Path.Combine(FolderPath, "fast");
|
||||
Directory.CreateDirectory(fast);
|
||||
|
||||
var exePath = CopyResourceToFile(TesseractResources.tesseract_x64, "tesseract.exe");
|
||||
var tesseractPath = CopyResourceToFile(TesseractResources.tesseract_x64, FolderPath, "tesseract.exe");
|
||||
CopyResourceToFile(TesseractResources.eng_traineddata, fast, "eng.traineddata");
|
||||
CopyResourceToFile(TesseractResources.heb_traineddata, fast, "heb.traineddata");
|
||||
_testImagePath = CopyResourceToFile(TesseractResources.ocr_test, "ocr_test.jpg");
|
||||
_testImagePathHebrew = CopyResourceToFile(TesseractResources.ocr_test_hebrew, "ocr_test_hebrew.jpg");
|
||||
|
||||
_engine = new TesseractOcrEngine(exePath, FolderPath);
|
||||
}
|
||||
|
||||
private string CopyResourceToFile(byte[] resource, string folder, string fileName)
|
||||
{
|
||||
string path = Path.Combine(folder, fileName);
|
||||
File.WriteAllBytes(path, resource);
|
||||
return path;
|
||||
}
|
||||
|
||||
private string CopyResourceToFile(byte[] resource, string fileName)
|
||||
{
|
||||
return CopyResourceToFile(resource, FolderPath, fileName);
|
||||
_engine = new TesseractOcrEngine(tesseractPath, FolderPath, FolderPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -22,7 +22,7 @@ namespace NAPS2.Sdk.Tests.Ocr {
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class TesseractResources {
|
||||
public class TesseractResources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using NAPS2.Images.Gdi;
|
||||
using NAPS2.Ocr;
|
||||
using NAPS2.Scan;
|
||||
using PdfSharp.Drawing;
|
||||
@ -193,7 +192,7 @@ public class PdfSharpExporter : PdfExporter
|
||||
page = document.AddPage();
|
||||
}
|
||||
|
||||
string tempImageFilePath = Path.Combine(Paths.Temp, Path.GetRandomFileName());
|
||||
string tempImageFilePath = Path.Combine(_scanningContext.TempFolderPath, Path.GetRandomFileName());
|
||||
|
||||
var format = image.Metadata.Lossless ? ImageFileFormat.Png : ImageFileFormat.Jpeg;
|
||||
using (var renderedImage = _scanningContext.ImageContext.Render(image))
|
||||
|
@ -45,12 +45,8 @@
|
||||
<Reference Include="PdfSharp">
|
||||
<HintPath>..\NAPS2.Setup\lib\PdfSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
|
||||
<PackageReference Include="BouncyCastle" Version="1.8.4" />
|
||||
<PackageReference Include="CommandLineParser" Version="1.9.71" />
|
||||
<PackageReference Include="Eto.Forms" Version="2.5.10" />
|
||||
<PackageReference Include="Eto.Platform.Windows" Version="2.5.10" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.26.0" PrivateAssets="all" />
|
||||
<PackageReference Include="GrpcDotNetNamedPipes" Version="1.4.2" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
|
||||
|
@ -5,24 +5,26 @@ namespace NAPS2.Ocr;
|
||||
|
||||
public class TesseractOcrEngine : IOcrEngine
|
||||
{
|
||||
private readonly string _exePath;
|
||||
private readonly string _tesseractPath;
|
||||
private readonly string? _languageDataBasePath;
|
||||
private readonly string _tempFolder;
|
||||
|
||||
public TesseractOcrEngine(string exePath, string? languageDataBasePath)
|
||||
public TesseractOcrEngine(string tesseractPath, string? languageDataBasePath, string tempFolder)
|
||||
{
|
||||
_exePath = exePath;
|
||||
_tesseractPath = tesseractPath;
|
||||
_languageDataBasePath = languageDataBasePath;
|
||||
_tempFolder = tempFolder;
|
||||
}
|
||||
|
||||
public async Task<OcrResult?> ProcessImage(string imagePath, OcrParams ocrParams, CancellationToken cancelToken)
|
||||
{
|
||||
string tempHocrFilePath = Path.Combine(Paths.Temp, Path.GetRandomFileName());
|
||||
string tempHocrFilePath = Path.Combine(_tempFolder, Path.GetRandomFileName());
|
||||
string tempHocrFilePathWithExt = tempHocrFilePath + ".hocr";
|
||||
try
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = _exePath,
|
||||
FileName = _tesseractPath,
|
||||
Arguments = $"\"{imagePath}\" \"{tempHocrFilePath}\" -l {ocrParams.LanguageCode} hocr",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
|
@ -5,18 +5,6 @@
|
||||
/// </summary>
|
||||
public abstract class OperationProgress
|
||||
{
|
||||
private static OperationProgress _default = new StubOperationProgress();
|
||||
|
||||
public static OperationProgress Default
|
||||
{
|
||||
get
|
||||
{
|
||||
TestingContext.NoStaticDefaults();
|
||||
return _default;
|
||||
}
|
||||
set => _default = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
public abstract void Attach(IOperation op);
|
||||
|
||||
public abstract void ShowProgress(IOperation op);
|
||||
|
@ -14,6 +14,10 @@ public interface ISystemCompat
|
||||
|
||||
bool IsWia20Supported { get; }
|
||||
|
||||
bool UseSystemTesseract { get; }
|
||||
|
||||
string? TesseractExecutablePath { get; }
|
||||
|
||||
string PdfiumLibraryPath { get; }
|
||||
|
||||
IntPtr LoadLibrary(string path);
|
||||
|
@ -19,6 +19,10 @@ public class LinuxSystemCompat : ISystemCompat
|
||||
|
||||
public bool UseUnixFontResolver => true;
|
||||
|
||||
public bool UseSystemTesseract => true;
|
||||
|
||||
public string? TesseractExecutablePath => null;
|
||||
|
||||
public string PdfiumLibraryPath => "_linux/libpdfium.so";
|
||||
|
||||
public IntPtr LoadLibrary(string path) => LinuxInterop.dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
|
||||
|
@ -19,6 +19,10 @@ public class MacSystemCompat : ISystemCompat
|
||||
|
||||
public bool UseUnixFontResolver => true;
|
||||
|
||||
public bool UseSystemTesseract => true;
|
||||
|
||||
public string? TesseractExecutablePath => null;
|
||||
|
||||
public string PdfiumLibraryPath => "_osx/libpdfium.dylib";
|
||||
|
||||
public IntPtr LoadLibrary(string path) => OsxInterop.dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
|
||||
|
@ -10,12 +10,6 @@ public static class Win32
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern int GetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int SetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar, int nPos, bool bRedraw);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
@ -4,6 +4,8 @@ namespace NAPS2.Platform;
|
||||
|
||||
public class Windows32SystemCompat : WindowsSystemCompat
|
||||
{
|
||||
public override string TesseractExecutablePath => "_win32/tesseract.exe";
|
||||
|
||||
public override string PdfiumLibraryPath => "_win32/pdfium.dll";
|
||||
|
||||
public override IntPtr LoadSymbol(IntPtr libraryHandle, string symbol)
|
||||
|
@ -4,6 +4,8 @@ namespace NAPS2.Platform;
|
||||
|
||||
public class Windows64SystemCompat : WindowsSystemCompat
|
||||
{
|
||||
public override string TesseractExecutablePath => "_win64/tesseract.exe";
|
||||
|
||||
public override string PdfiumLibraryPath => "_win64/pdfium.dll";
|
||||
|
||||
public override IntPtr LoadSymbol(IntPtr libraryHandle, string symbol)
|
||||
|
@ -17,6 +17,10 @@ public abstract class WindowsSystemCompat : ISystemCompat
|
||||
|
||||
public bool UseUnixFontResolver => false;
|
||||
|
||||
public bool UseSystemTesseract => false;
|
||||
|
||||
public abstract string? TesseractExecutablePath { get; }
|
||||
|
||||
public abstract string PdfiumLibraryPath { get; }
|
||||
|
||||
public IntPtr LoadLibrary(string path) => Win32.LoadLibrary(path);
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Windows.Forms;
|
||||
using NAPS2.Ocr;
|
||||
using NAPS2.Remoting.Worker;
|
||||
|
||||
@ -33,8 +32,7 @@ public class ScanningContext : IDisposable
|
||||
|
||||
public FileStorageManager? FileStorageManager { get; set; }
|
||||
|
||||
// TODO: Rethink how this works.
|
||||
public string TempFolderPath { get; set; }
|
||||
public string TempFolderPath { get; set; } = Path.GetTempPath();
|
||||
|
||||
public IWorkerFactory WorkerFactory { get; set; }
|
||||
|
||||
|
@ -64,7 +64,7 @@ public interface ISelectable<T>
|
||||
ListSelection<T> Selection { get; set; }
|
||||
}
|
||||
|
||||
public class Selectable<T>
|
||||
public class Selectable<T> : ISelectable<T>
|
||||
{
|
||||
private ListSelection<T> _selection = ListSelection.Empty<T>();
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace NAPS2.Util;
|
||||
namespace NAPS2.Util;
|
||||
|
||||
// TODO: Refactor to Eto and move to NAPS2.EtoForms (or something non-eto and non-winforms if I want operations in the Sdk...)
|
||||
/// <summary>
|
||||
@ -17,5 +15,5 @@ public abstract class OverwritePrompt
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file to overwrite.</param>
|
||||
/// <returns>Yes, No, or Cancel.</returns>
|
||||
public abstract DialogResult ConfirmOverwrite(string path);
|
||||
public abstract OverwriteResponse ConfirmOverwrite(string path);
|
||||
}
|
8
NAPS2.Sdk/Util/OverwriteResponse.cs
Normal file
8
NAPS2.Sdk/Util/OverwriteResponse.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace NAPS2.Util;
|
||||
|
||||
public enum OverwriteResponse
|
||||
{
|
||||
Yes,
|
||||
No,
|
||||
Abort
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace NAPS2.Util;
|
||||
namespace NAPS2.Util;
|
||||
|
||||
public class StubOverwritePrompt : OverwritePrompt
|
||||
{
|
||||
public override DialogResult ConfirmOverwrite(string path) => DialogResult.No;
|
||||
public override OverwriteResponse ConfirmOverwrite(string path) => OverwriteResponse.No;
|
||||
}
|
Loading…
Reference in New Issue
Block a user