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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions NAPS2.Lib/EtoForms/Desktop/Sidebar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,7 @@ private void UpdateUiForProfile()
_bitDepth.SelectedItem = profile.BitDepth;
_resolution!.SetDpi(profile.Resolution.Dpi);

_paperSource.Items = profile.Caps?.PaperSources?.Values is [_, ..] paperSources
? paperSources
: EnumDropDownWidget<ScanSource>.DefaultItems;
_paperSource.Items = ScanSourceHelper.GetCompatibleScanSources(profile, deviceDriver);

var selectedSource = _paperSource.SelectedItem;
var perSource = selectedSource switch
Expand Down
5 changes: 2 additions & 3 deletions NAPS2.Lib/EtoForms/Ui/EditProfileForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,7 @@ private void UpdateUiForCaps()
{
_suppressChangeEvent = true;

_paperSource.Items = ScanProfile.Caps?.PaperSources?.Values is [_, ..] paperSources
? paperSources
: EnumDropDownWidget<ScanSource>.DefaultItems;
_paperSource.Items = ScanSourceHelper.GetCompatibleScanSources(ScanProfile, DeviceDriver);

var selectedSource = _paperSource.SelectedItem;
var perSource = selectedSource switch
Expand Down Expand Up @@ -234,6 +232,7 @@ private ScanProfileCaps MapCaps(ScanCaps? caps)
if (paperSourceCaps.SupportsFlatbed) paperSources.Add(ScanSource.Glass);
if (paperSourceCaps.SupportsFeeder) paperSources.Add(ScanSource.Feeder);
if (paperSourceCaps.SupportsDuplex) paperSources.Add(ScanSource.Duplex);
if (paperSourceCaps is { SupportsFeeder: true, SupportsFlatbed: true, CanCheckIfFeederHasPaper: true }) paperSources.Add(ScanSource.Auto);
}

return new ScanProfileCaps
Expand Down
9 changes: 9 additions & 0 deletions NAPS2.Lib/Lang/Resources/SettingsResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions NAPS2.Lib/Lang/Resources/SettingsResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@
<data name="Scale_1_8" xml:space="preserve">
<value>1:8</value>
</data>
<data name="Source_Auto" xml:space="preserve">
<value>Auto</value>
</data>
<data name="Source_Duplex" xml:space="preserve">
<value>Duplex</value>
</data>
Expand Down
6 changes: 5 additions & 1 deletion NAPS2.Lib/Scan/ScanProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ public enum ScanSource
[LocalizedDescription(typeof(SettingsResources), "Source_Feeder")]
Feeder,
[LocalizedDescription(typeof(SettingsResources), "Source_Duplex")]
Duplex
Duplex,
[LocalizedDescription(typeof(SettingsResources), "Source_Auto")]
Auto
}

/// <summary>
Expand Down Expand Up @@ -437,6 +439,8 @@ public static PaperSource ToPaperSource(this ScanSource scanSource)
return PaperSource.Feeder;
case ScanSource.Duplex:
return PaperSource.Duplex;
case ScanSource.Auto:
return PaperSource.Auto;
default:
throw new ArgumentException();
}
Expand Down
16 changes: 16 additions & 0 deletions NAPS2.Lib/Util/ScanSourceHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using NAPS2.EtoForms.Widgets;
using NAPS2.Scan;

namespace NAPS2.Util;

public static class ScanSourceHelper
{
public static List<ScanSource> GetCompatibleScanSources(ScanProfile profile, Driver deviceDriver)
{
if(profile.Caps?.PaperSources?.Values is not { } scanSources)
scanSources = [.. EnumDropDownWidget<ScanSource>.DefaultItems];
if (deviceDriver is not (Driver.Wia or Driver.Twain or Driver.Escl))
scanSources.Remove(ScanSource.Auto);
return scanSources;
}
}
16 changes: 11 additions & 5 deletions NAPS2.Sdk/Scan/Internal/Escl/EsclScanDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public async Task Scan(ScanOptions options, CancellationToken cancelToken, IScan
bool hasErrorDetailsExtension = caps.Naps2Extensions?.Contains("ErrorDetails") ?? false;
bool hasShortTimeoutExtension = caps.Naps2Extensions?.Contains("ShortTimeout") ?? false;
bool hasAnyDpiExtension = caps.Naps2Extensions?.Contains("AnyDpi") ?? false;
var scanSettings = GetScanSettings(options, caps, hasAnyDpiExtension);
var scanSettings = GetScanSettings(options, caps, hasAnyDpiExtension, status);
Action<double>? progressCallback = hasProgressExtension ? scanEvents.PageProgress : null;

if (cancelToken.IsCancellationRequested) return;
Expand Down Expand Up @@ -441,7 +441,7 @@ private IEnumerable<IMemoryImage> GetImagesFromRawDocument(ScanOptions options,
}
}

private EsclScanSettings GetScanSettings(ScanOptions options, EsclCapabilities caps, bool hasAnyDpiExtension)
private EsclScanSettings GetScanSettings(ScanOptions options, EsclCapabilities caps, bool hasAnyDpiExtension, EsclScannerStatus status)
{
if (options.PaperSource == PaperSource.Feeder && caps.AdfSimplexCaps == null)
{
Expand All @@ -451,10 +451,16 @@ private EsclScanSettings GetScanSettings(ScanOptions options, EsclCapabilities c
{
throw new NoDuplexSupportException();
}
if (options.PaperSource is PaperSource.Flatbed or PaperSource.Auto
&& caps.PlatenCaps == null && caps.AdfSimplexCaps != null)

if (options.PaperSource == PaperSource.Auto)
{
options.PaperSource = PaperSource.Feeder;
options.PaperSource = caps switch
{
{ PlatenCaps: not null, AdfSimplexCaps: null } => PaperSource.Flatbed,
{ PlatenCaps: null, AdfSimplexCaps: not null } => PaperSource.Feeder,
_ when status is { AdfState: not EsclAdfState.ScannerAdfEmpty } => PaperSource.Feeder,
_ => PaperSource.Flatbed
};
}

var (inputCaps, inputSource, duplex) = options.PaperSource switch
Expand Down
35 changes: 20 additions & 15 deletions NAPS2.Sdk/Scan/Internal/Twain/TwainScanRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,26 @@ private void ConfigureSource(DataSource source)
// Paper Source
switch (_options.PaperSource)
{
case PaperSource.Auto: // Assume the data source will ignore if unsupported
case PaperSource.Auto:
if (source.Capabilities.CapFeederEnabled.IsSupported)
{
source.Capabilities.CapFeederEnabled.SetValue(BoolType.True);
}
if (source.Capabilities.CapAutomaticSenseMedium.IsSupported)
{
source.Capabilities.CapAutomaticSenseMedium.SetValue(BoolType.True);
}
else
{
// Only set if CAP_AUTOMATICSENSEMEDIUM is unsupported (https://epson.com/Support/wa00954e?pg=6#:~:text=CAP_AUTOMATICSENSEMEDIUM,to%20CAP_FEEDERENABLED%20dynamically.)
// CAP_FEEDERLOADED is depending on CAP_FEEDERENABLED being set to TRUE (https://epson.com/Support/wa00954e?pg=6#:~:text=TWCC_BADCAP.-,CAP_FEEDERLOADED,TRUE.,-CAP_FEEDPAGE)
if (!source.Capabilities.CapFeederLoaded.IsSupported || source.Capabilities.CapFeederLoaded.GetCurrent() == BoolType.False)
{
source.Capabilities.CapFeederEnabled.SetValue(BoolType.False);
}
source.Capabilities.CapDuplexEnabled.SetValue(BoolType.False);
}
break;
case PaperSource.Flatbed:
source.Capabilities.CapFeederEnabled.SetValue(BoolType.False);
source.Capabilities.CapDuplexEnabled.SetValue(BoolType.False);
Expand All @@ -351,20 +370,6 @@ private void ConfigureSource(DataSource source)
break;
}

// TODO: Should we add an "Automatic" option in the NAPS2 GUI instead of making "Glass" = Auto?
// For "Auto", choose the feeder if it has paper, otherwise the flatbed.
if (_options.PaperSource == PaperSource.Auto)
{
if (source.Capabilities.CapAutomaticSenseMedium.IsSupported)
{
source.Capabilities.CapAutomaticSenseMedium.SetValue(BoolType.True);
}
else if (source.Capabilities.CapFeederLoaded.IsSupported &&
source.Capabilities.CapFeederLoaded.GetCurrent() == BoolType.True)
{
source.Capabilities.CapFeederEnabled.SetValue(BoolType.True);
}
}

// Bit Depth
switch (_options.BitDepth)
Expand Down
51 changes: 27 additions & 24 deletions NAPS2.Sdk/Scan/Internal/Wia/WiaScanDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,21 +172,18 @@ public async Task Scan(WiaVersion wiaVersion)
return;
}

if (_options.PaperSource == PaperSource.Auto)
if (_options.PaperSource != PaperSource.Auto)
{
// Default to flatbed if supported (or if both support checks fail)
_options.PaperSource = device.SupportsFlatbed() || !device.SupportsFeeder()
? PaperSource.Flatbed
: PaperSource.Feeder;
if (!DoTransferWithPaperSource(device, _options.PaperSource) && !_cancelToken.IsCancellationRequested &&
_options.PaperSource != PaperSource.Flatbed)
throw new DeviceFeederEmptyException();
}

using var item = GetItem(device);
if (item == null)
else
{
return;
if (!device.SupportsFeeder() || !device.FeederReady() ||
!DoTransferWithPaperSource(device, PaperSource.Feeder))
DoTransferWithPaperSource(device, PaperSource.Flatbed);
}

DoTransfer(device, item);
}

private async Task DoWia20NativeTransfer(WiaDeviceManager deviceManager, WiaDevice device)
Expand Down Expand Up @@ -230,7 +227,13 @@ private async Task DoWia20NativeTransfer(WiaDeviceManager deviceManager, WiaDevi
}
}

private void DoTransfer(WiaDevice device, WiaItem item)
private bool DoTransferWithPaperSource(WiaDevice device, PaperSource source)
{
using var item = GetItem(device, source);
return item != null && DoTransfer(device, item);
}

private bool DoTransfer(WiaDevice device, WiaItem item)
{
if (_options.PaperSource != PaperSource.Flatbed && !device.SupportsFeeder())
{
Expand Down Expand Up @@ -283,7 +286,6 @@ private void DoTransfer(WiaDevice device, WiaItem item)
transfer.Progress += (sender, args) => _scanEvents.PageProgress(args.Percent / 100.0);
using (_cancelToken.Register(transfer.Cancel))
{
_scanEvents.PageStart();
try
{
transfer.Download();
Expand All @@ -292,6 +294,15 @@ private void DoTransfer(WiaDevice device, WiaItem item)
{
// This error code is undocumented but seems to mean "no more pages" which can be ignored
}
catch (WiaException e) when (e.ErrorCode == 0x801901F4 && _options.PaperSource != PaperSource.Flatbed)
{
// This error code isnt specific to wia but is returned when attempting to scan with an empty feeder
// This is more reliable than WIA_DPS_DOCUMENT_HANDLING_STATUS & FEED_READY, because in some models its used to indicate a feeder is "installed" not "has pages in it"
return hasAtLeastOneImage;
}

_scanEvents.PageStart();


if (device.Version == WiaVersion.Wia10 && _options.PaperSource != PaperSource.Flatbed)
{
Expand All @@ -308,18 +319,10 @@ private void DoTransfer(WiaDevice device, WiaItem item)
}
}
}
if (scanException != null)
{
throw scanException;
}
if (!hasAtLeastOneImage && !_cancelToken.IsCancellationRequested &&
_options.PaperSource != PaperSource.Flatbed)
{
throw new DeviceFeederEmptyException();
}
return scanException != null ? throw scanException : hasAtLeastOneImage;
}

private WiaItem? GetItem(WiaDevice device)
private WiaItem? GetItem(WiaDevice device, PaperSource source)
{
if (_options.UseNativeUI)
{
Expand Down Expand Up @@ -369,7 +372,7 @@ private void DoTransfer(WiaDevice device, WiaItem item)
// The "Feeder" child may also have a pair of children (for front/back sides with duplex)
// https://docs.microsoft.com/en-us/windows-hardware/drivers/image/simple-duplex-capable-document-feeder
var items = device.GetSubItems();
var preferredItemName = _options.PaperSource == PaperSource.Flatbed ? "Flatbed" : "Feeder";
var preferredItemName = source == PaperSource.Flatbed ? "Flatbed" : "Feeder";
return items.FirstOrDefault(x => x.Name() == preferredItemName) ?? items.First();
}
}
Expand Down