using NTwain.Data;
using NTwain.Values;
using NTwain.Values.Cap;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NTwain
{
///
/// Defines common methods on using the raw
/// TWAIN triplet api.
///
public static class TwainSessionExtensions
{
///
/// Gets the manager status. Only call this at state 2 or higher.
///
/// The session.
///
public static TWStatus GetManagerStatus(this TwainSession session)
{
TWStatus stat;
session.DGControl.Status.GetManager(out stat);
return stat;
}
///
/// Gets the source status. Only call this at state 4 or higher.
///
/// The session.
///
public static TWStatus GetSourceStatus(this TwainSession session)
{
TWStatus stat;
session.DGControl.Status.GetSource(out stat);
return stat;
}
///
/// Gets list of sources available in the system.
/// Only call this at state 2 or higher.
///
/// The session.
///
public static IList GetSources(this TwainSession session)
{
List list = new List();
// now enumerate
TWIdentity srcId;
var rc = session.DGControl.Identity.GetFirst(out srcId);
if (rc == ReturnCode.Success) { list.Add(srcId); }
do
{
rc = session.DGControl.Identity.GetNext(out srcId);
if (rc == ReturnCode.Success)
{
list.Add(srcId);
}
} while (rc == ReturnCode.Success);
return list;
}
#region common caps
///
/// Gets the current value for a general capability. This only works for types that are under 32bit.
///
///
/// The session.
/// The cap id.
///
public static T GetCurrentCap(this TwainSession session, CapabilityId capId) where T : struct,IConvertible
{
using (TWCapability cap = new TWCapability(capId))
{
var rc = session.DGControl.Capability.GetCurrent(cap);
if (rc == ReturnCode.Success)
{
switch (cap.ContainerType)
{
case ContainerType.Enum:
var enu = cap.GetEnumValue();
if (enu.ItemType < ItemType.Frame)
{
// does this work?
return ConvertValueToType(enu.ItemList[enu.CurrentIndex].ToString(), true);
}
break;
case ContainerType.OneValue:
var one = cap.GetOneValue();
if (one.ItemType < ItemType.Frame)
{
return ConvertValueToType(one.Item, true);
}
break;
case ContainerType.Range:
var range = cap.GetRangeValue();
if (range.ItemType < ItemType.Frame)
{
return ConvertValueToType(range.CurrentValue, true);
}
break;
}
}
}
return default(T);
}
static ushort GetLowerWord(uint value)
{
return (ushort)(value & 0xffff);
}
static uint GetUpperWord(uint value)
{
return (ushort)(value >> 16);
}
static T ConvertValueToType(object value, bool tryUpperWord) where T : struct,IConvertible
{
var returnType = typeof(T);
if (returnType.IsEnum)
{
if (tryUpperWord)
{
// small routine to see if works with bad sources that put
// 16bit value in the upper word instead of lower word.
var rawType = Enum.GetUnderlyingType(returnType);
if (typeof(ushort).IsAssignableFrom(rawType))
{
var intVal = Convert.ToUInt32(value);
var enumVal = GetLowerWord(intVal);
if (!Enum.IsDefined(returnType, enumVal))
{
return (T)Enum.ToObject(returnType, GetUpperWord(intVal));
}
}
}
// this may work better?
return (T)Enum.ToObject(returnType, value);
//// cast to underlying type first then to the enum
//return (T)Convert.ChangeType(value, rawType);
}
return (T)Convert.ChangeType(value, returnType);
}
///
/// A generic method that returns the data in a .
///
/// The expected capability value type.
/// The capability returned from the source.
/// The list to populate if necessary.
///
public static IList ReadMultiCapValues(this TWCapability capability, IList toPopulate) where TCapVal : struct,IConvertible
{
return ReadMultiCapValues(capability, toPopulate, true);
}
static IList ReadMultiCapValues(this TWCapability capability, IList toPopulate, bool tryUpperWord) where TCapVal : struct,IConvertible
{
if (toPopulate == null) { toPopulate = new List(); }
switch (capability.ContainerType)
{
case ContainerType.OneValue:
var value = capability.GetOneValue();
if (value != null)
{
var val = ConvertValueToType(value.Item, tryUpperWord);// (T)Convert.ToUInt16(value.Item);
toPopulate.Add(val);
}
break;
case ContainerType.Array:
var arr = capability.GetArrayValue();
if (arr != null && arr.ItemList != null)
{
for (int i = 0; i < arr.ItemList.Length; i++)
{
var val = ConvertValueToType(arr.ItemList[i], tryUpperWord);// (T)Convert.ToUInt16(enumr.ItemList[i]);
toPopulate.Add(val);
}
}
break;
case ContainerType.Enum:
var enumr = capability.GetEnumValue();
if (enumr != null && enumr.ItemList != null)
{
for (int i = 0; i < enumr.ItemList.Length; i++)
{
var val = ConvertValueToType(enumr.ItemList[i], tryUpperWord);// (T)Convert.ToUInt16(enumr.ItemList[i]);
toPopulate.Add(val);
}
}
break;
case ContainerType.Range:
var range = capability.GetRangeValue();
if (range != null)
{
for (uint i = range.MinValue; i < range.MaxValue; i += range.StepSize)
{
var val = ConvertValueToType(i, tryUpperWord);
toPopulate.Add(val);
}
}
break;
}
return toPopulate;
}
///
/// A generic method that tries to get capability values from current .
///
/// The expected capability value type.
/// The session.
/// The capability unique identifier.
/// if set to true then apply to workaround for certain bad sources.
///
public static IList GetCapabilityValues(this TwainSession session, CapabilityId capabilityId, bool tryUpperWord) where TCapVal : struct,IConvertible
{
var list = new List();
using (TWCapability cap = new TWCapability(capabilityId))
{
var rc = session.DGControl.Capability.Get(cap);
if (rc == ReturnCode.Success)
{
cap.ReadMultiCapValues(list, tryUpperWord);
}
}
return list;
}
///
/// Gets list of capabilities supported by current source.
/// Only call this at state 4 or higher.
///
/// The session.
///
internal static IList GetCapabilities(this TwainSession session)
{
return session.GetCapabilityValues(CapabilityId.CapSupportedCaps, false);
}
#region xfer mech
///
/// Gets the supported image for the current source.
/// Only call this at state 4 or higher.
///
/// The session.
///
public static IList CapGetImageXferMech(this TwainSession session)
{
return session.GetCapabilityValues(CapabilityId.ICapXferMech, true);
}
#endregion
#region compression
///
/// Gets the supported for the current source.
/// Only call this at state 4 or higher.
///
/// The session.
///
public static IList CapGetCompression(this TwainSession session)
{
return session.GetCapabilityValues(CapabilityId.ICapCompression, true);
}
///
/// Change the image compression for the current source.
///
/// The session.
/// The compression.
///
public static ReturnCode CapSetImageCompression(this TwainSession session, Compression compression)
{
using (TWCapability compressCap = new TWCapability(CapabilityId.ICapCompression, new TWOneValue { Item = (uint)compression, ItemType = Values.ItemType.UInt16 }))
{
return session.DGControl.Capability.Set(compressCap);
}
}
#endregion
#region image format
///
/// Gets the supported for the current source.
/// Only call this at state 4 or higher.
///
/// The session.
///
public static IList CapGetImageFileFormat(this TwainSession session)
{
return session.GetCapabilityValues(CapabilityId.ICapImageFileFormat, true);
}
///
/// Change the image format for the current source.
///
/// The session.
/// The format.
///
public static ReturnCode CapSetImageFormat(this TwainSession session, ImageFileFormat format)
{
using (TWCapability formatCap = new TWCapability(CapabilityId.ICapImageFileFormat, new TWOneValue { Item = (uint)format, ItemType = Values.ItemType.UInt16 }))
{
return session.DGControl.Capability.Set(formatCap);
}
}
#endregion
#region pixel type
///
/// Gets the supported for the current source.
/// Only call this at state 4 or higher.
///
/// The session.
///
public static IList CapGetPixelTypes(this TwainSession session)
{
return session.GetCapabilityValues(CapabilityId.ICapPixelType, true);
}
///
/// Change the pixel type for the current source.
///
/// The session.
/// The type.
///
public static ReturnCode CapSetPixelType(this TwainSession session, PixelType type)
{
var one = new TWOneValue();
one.Item = (uint)type;
one.ItemType = ItemType.UInt16;
using (TWCapability dx = new TWCapability(CapabilityId.ICapPixelType, one))
{
return session.DGControl.Capability.Set(dx);
}
}
#endregion
#region dpi
///
/// Gets the supported DPI values for the current source.
/// Only call this at state 4 or higher.
///
/// The session.
///
public static IList CapGetDPIs(this TwainSession session)
{
return session.GetCapabilityValues(CapabilityId.ICapXResolution, true);
}
///
/// Change the DPI value for the current source.
///
/// The session.
/// The DPI.
///
public static ReturnCode CapSetDPI(this TwainSession session, int dpi)
{
return CapSetDPI(session, dpi, dpi);
}
///
/// Change the DPI value for the current source.
///
/// The session.
/// The x DPI.
/// The y DPI.
///
public static ReturnCode CapSetDPI(this TwainSession session, int xDPI, int yDPI)
{
TWOneValue one = new TWOneValue();
one.Item = (uint)xDPI;// ((uint)dpi) << 16;
one.ItemType = ItemType.Fix32;
using (TWCapability xres = new TWCapability(CapabilityId.ICapXResolution, one))
{
var rc = session.DGControl.Capability.Set(xres);
if (rc == ReturnCode.Success)
{
one.Item = (uint)yDPI;
using (TWCapability yres = new TWCapability(CapabilityId.ICapYResolution, one))
{
rc = session.DGControl.Capability.Set(yres);
}
}
return rc;
}
}
#endregion
#region supported paper size
///
/// Gets the supported for the current source.
/// Only call this at state 4 or higher.
///
/// The session.
///
public static IList CapGetSupportedSizes(this TwainSession session)
{
return session.GetCapabilityValues(CapabilityId.ICapSupportedSizes, true);
}
///
/// Change the supported paper size for the current source.
///
/// The session.
/// The size.
///
public static ReturnCode CapSetSupportedSize(this TwainSession session, SupportedSize size)
{
var one = new TWOneValue();
one.Item = (uint)size;
one.ItemType = ItemType.UInt16;
using (TWCapability xres = new TWCapability(CapabilityId.ICapSupportedSizes, one))
{
var rc = session.DGControl.Capability.Set(xres);
return rc;
}
}
#endregion
#region onesie flags
///
/// Change the auto deskew flag for the current source.
///
/// The session.
/// if set to true [use it].
///
public static ReturnCode CapSetAutoDeskew(this TwainSession session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.ICapAutomaticDeskew))
{
if (session.SourceId.ProtocolMajor >= 2)
{
// if using twain 2.0 will need to use enum instead of onevalue (yuck)
TWEnumeration en = new TWEnumeration();
en.ItemList = new object[] { (uint)(useIt ? 1 : 0) };
en.ItemType = ItemType.Bool;
using (TWCapability dx = new TWCapability(CapabilityId.ICapAutomaticDeskew, en))
{
rc = session.DGControl.Capability.Set(dx);
}
}
else
{
TWOneValue one = new TWOneValue();
one.Item = (uint)(useIt ? 1 : 0);
one.ItemType = ItemType.Bool;
using (TWCapability capValue = new TWCapability(CapabilityId.ICapAutomaticDeskew, one))
{
rc = session.DGControl.Capability.Set(capValue);
}
}
}
return rc;
}
///
/// Change the auto rotate flag for the current source.
///
/// The session.
/// if set to true [use it].
///
public static ReturnCode CapSetAutoRotate(this TwainSession session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.ICapAutomaticRotate))
{
if (session.SourceId.ProtocolMajor >= 2)
{
// if using twain 2.0 will need to use enum instead of onevalue (yuck)
TWEnumeration en = new TWEnumeration();
en.ItemList = new object[] { (uint)(useIt ? 1 : 0) };
en.ItemType = ItemType.Bool;
using (TWCapability dx = new TWCapability(CapabilityId.ICapAutomaticRotate, en))
{
rc = session.DGControl.Capability.Set(dx);
}
}
else
{
TWOneValue one = new TWOneValue();
one.Item = (uint)(useIt ? 1 : 0);
one.ItemType = ItemType.Bool;
using (TWCapability capValue = new TWCapability(CapabilityId.ICapAutomaticRotate, one))
{
rc = session.DGControl.Capability.Set(capValue);
}
}
}
return rc;
}
///
/// Change the auto border detection flag for the current source.
///
/// The session.
/// if set to true [use it].
///
public static ReturnCode CapSetBorderDetection(this TwainSession session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.ICapAutomaticBorderDetection))
{
// this goes along with undefinedimagesize so that also
// needs to be set
if (session.SourceId.ProtocolMajor >= 2)
{
// if using twain 2.0 will need to use enum instead of onevalue (yuck)
TWEnumeration en = new TWEnumeration();
en.ItemList = new object[] { (uint)(useIt ? 1 : 0) };
en.ItemType = ItemType.Bool;
using (TWCapability dx = new TWCapability(CapabilityId.ICapUndefinedImageSize, en))
{
rc = session.DGControl.Capability.Set(dx);
}
using (TWCapability dx = new TWCapability(CapabilityId.ICapAutomaticBorderDetection, en))
{
rc = session.DGControl.Capability.Set(dx);
}
}
else
{
TWOneValue one = new TWOneValue();
one.Item = (uint)(useIt ? 1 : 0);
one.ItemType = ItemType.Bool;
using (TWCapability capValue = new TWCapability(CapabilityId.ICapUndefinedImageSize, one))
{
rc = session.DGControl.Capability.Set(capValue);
}
using (TWCapability capValue = new TWCapability(CapabilityId.ICapAutomaticBorderDetection, one))
{
rc = session.DGControl.Capability.Set(capValue);
}
}
}
return rc;
}
///
/// Change the duplex flag for the current source.
///
/// The session.
/// if set to true [use it].
///
public static ReturnCode CapSetDuplex(this TwainSession session, bool useIt)
{
if (session.SourceId.ProtocolMajor >= 2)
{
// twain 2 likes to use enum :(
TWEnumeration en = new TWEnumeration();
en.ItemList = new object[] { (uint)(useIt ? 1 : 0) };
en.ItemType = ItemType.Bool;
using (TWCapability dx = new TWCapability(CapabilityId.CapDuplexEnabled, en))
{
return session.DGControl.Capability.Set(dx);
}
}
else
{
TWOneValue one = new TWOneValue();
one.Item = (uint)(useIt ? 1 : 0);
one.ItemType = ItemType.Bool;
using (TWCapability dx = new TWCapability(CapabilityId.CapDuplexEnabled, one))
{
return session.DGControl.Capability.Set(dx);
}
}
}
///
/// Change the use feeder flag for the current source.
///
/// The session.
/// if set to true [use it].
///
public static ReturnCode CapSetFeeder(this TwainSession session, bool useIt)
{
var rc = ReturnCode.Failure;
if (session.SupportedCaps.Contains(CapabilityId.CapFeederEnabled))
{
if (session.SourceId.ProtocolMajor >= 2)
{
// if using twain 2.0 will need to use enum instead of onevalue (yuck)
TWEnumeration en = new TWEnumeration();
en.ItemList = new object[] { (ushort)(useIt ? 1 : 0) };
en.ItemType = ItemType.Bool;
// we will never set feeder off, only autofeed and autoscan
// but if it is to SET then enable feeder needs to be set first
if (useIt)
{
using (TWCapability dx = new TWCapability(CapabilityId.CapFeederEnabled, en))
{
rc = session.DGControl.Capability.Set(dx);
}
}
// to really use feeder we must also set autofeed or autoscan, but only
// for one of them since setting autoscan also sets autofeed
if (session.SupportedCaps.Contains(CapabilityId.CapAutoScan))
{
using (TWCapability dx = new TWCapability(CapabilityId.CapAutoScan, en))
{
rc = session.DGControl.Capability.Set(dx);
}
}
else if (session.SupportedCaps.Contains(CapabilityId.CapAutoFeed))
{
using (TWCapability dx = new TWCapability(CapabilityId.CapAutoFeed, en))
{
rc = session.DGControl.Capability.Set(dx);
}
}
}
else
{
TWOneValue one = new TWOneValue();
one.Item = (uint)(useIt ? 1 : 0);
one.ItemType = ItemType.Bool;
if (useIt)
{
using (TWCapability enabled = new TWCapability(CapabilityId.CapFeederEnabled, one))
{
rc = session.DGControl.Capability.Set(enabled);
}
}
// to really use feeder we must also set autofeed or autoscan, but only
// for one of them since setting autoscan also sets autofeed
if (session.SupportedCaps.Contains(CapabilityId.CapAutoScan))
{
using (TWCapability autoScan = new TWCapability(CapabilityId.CapAutoScan, one))
{
rc = session.DGControl.Capability.Set(autoScan);
}
}
else if (session.SupportedCaps.Contains(CapabilityId.CapAutoFeed))
{
using (TWCapability autoScan = new TWCapability(CapabilityId.CapAutoFeed, one))
{
rc = session.DGControl.Capability.Set(autoScan);
}
}
}
}
return rc;
}
#endregion
#endregion
}
}