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 } }