0
votes

I'm trying to scan A4 pages from a C# application using a flat bed scanner and Windows 10. To speed things up, I use the ScanWIA library found here: https://scanwia.codeplex.com/

However, I have huge problems configuring the page settings correctly.

  • Setting the page size (WIA ID 3097) to AUTO, gives me an "property not supported" exception.
  • Setting horizontal and vertical extend (WIA ID 6151, 6152) gives me either too small (cropped) results or an "value out of range" exception.

What is the correct way to set this up for A4 pages and variable DPI settings? How do I set the size of the captured area correctly? How do I control the size of the output image? Which setting uses which unit? What are the maximum value ranges?

MSDN is not very helpful on these topics...

1

1 Answers

4
votes

I have made a user control to scan the documents from attached scanner.
Here I can explain the details.

Screenshot demo app

In the attached form image there are 2 picture boxes, a preview button, a save button, two labels for showing save path and a drop down to show available scanner devices. See the image above to get a clear picture about the form design.

There is a class file named WIAScanner.cs, see the the code below

using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
using WIA;
namespace TESTSCAN
{
    class WIAScanner
    {
        const string wiaFormatBMP = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}";
        const string WIA_DEVICE_PROPERTY_PAGES_ID = "3096";
        const string WIA_SCAN_BRIGHTNESS_PERCENTS = "6154";
        const string WIA_SCAN_CONTRAST_PERCENTS = "6155";
        const string WIA_SCAN_COLOR_MODE = "6146";
        class WIA_DPS_DOCUMENT_HANDLING_SELECT
        {
            public const uint FEEDER = 0x00000001;
            public const uint FLATBED = 0x00000002;
        }
        class WIA_DPS_DOCUMENT_HANDLING_STATUS
        {
            public const uint FEED_READY = 0x00000001;
        }
        class WIA_PROPERTIES
        {
            public const uint WIA_RESERVED_FOR_NEW_PROPS = 1024;
            public const uint WIA_DIP_FIRST = 2;
            public const uint WIA_DPA_FIRST = WIA_DIP_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
            public const uint WIA_DPC_FIRST = WIA_DPA_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
            //
            // Scanner only device properties (DPS)
            //
            public const uint WIA_DPS_FIRST = WIA_DPC_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
            public const uint WIA_DPS_DOCUMENT_HANDLING_STATUS = WIA_DPS_FIRST + 13;
            public const uint WIA_DPS_DOCUMENT_HANDLING_SELECT = WIA_DPS_FIRST + 14;

        }
        //public void SetProperty(Property property, int value)
        //{
        //    IProperty x = (IProperty)property;
        //    Object val = value;
        //    x.set_Value(ref val);
        //}

        /// <summary>
        /// Use scanner to scan an image (with user selecting the scanner from a dialog).
        /// </summary>
        /// <returns>Scanned images.</returns>
        public static List<Image> Scan()
        {
            WIA.ICommonDialog dialog = new WIA.CommonDialog();
            WIA.Device device = dialog.ShowSelectDevice(WIA.WiaDeviceType.UnspecifiedDeviceType, true, false);
            if (device != null)
            {

                return Scan(device.DeviceID,1);
            }
            else
            {
                throw new Exception("You must select a device for scanning.");
            }
        }

        /// <summary>
        /// Use scanner to scan an image (scanner is selected by its unique id).
        /// </summary>
        /// <param name="scannerName"></param>
        /// <returns>Scanned images.</returns>
        public static List<Image> Scan(string scannerId, int pages)
        {
            List<Image> images = new List<Image>();
            bool hasMorePages = true;
            int numbrPages = pages;
            while (hasMorePages)
            {
                // select the correct scanner using the provided scannerId parameter
                WIA.DeviceManager manager = new WIA.DeviceManager();
                WIA.Device device = null;
                foreach (WIA.DeviceInfo info in manager.DeviceInfos)
                {
                    if (info.DeviceID == scannerId)
                    {
                        // connect to scanner
                        device = info.Connect();
                        break;
                    }
                }
                // device was not found
                if (device == null)
                {
                    // enumerate available devices
                    string availableDevices = "";
                    foreach (WIA.DeviceInfo info in manager.DeviceInfos)
                    {
                        availableDevices += info.DeviceID + "\n";
                    }

                    // show error with available devices
                    throw new Exception("The device with provided ID could not be found. Available Devices:\n" + availableDevices);
                }
                SetWIAProperty(device.Properties, WIA_DEVICE_PROPERTY_PAGES_ID, 1);
                WIA.Item item = device.Items[1] as WIA.Item;
                AdjustScannerSettings(item, 150, 0, 0, 1250, 1700, 0, 0, 1);
                try
                {
                    // scan image
                    WIA.ICommonDialog wiaCommonDialog = new WIA.CommonDialog();
                    WIA.ImageFile image = (WIA.ImageFile)wiaCommonDialog.ShowTransfer(item, wiaFormatBMP, false);

                    // save to temp file
                    string fileName = Path.GetTempFileName();
                    File.Delete(fileName);
                    image.SaveFile(fileName);
                    image = null;
                    // add file to output list
                    images.Add(Image.FromFile(fileName));
                }
                catch (Exception exc)
                {
                    throw exc;
                }
                finally
                {
                    item = null;
                    //determine if there are any more pages waiting
                    WIA.Property documentHandlingSelect = null;
                    WIA.Property documentHandlingStatus = null;
                    foreach (WIA.Property prop in device.Properties)
                    {
                        if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_SELECT)
                            documentHandlingSelect = prop;
                        if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_STATUS)
                            documentHandlingStatus = prop;
                    }
                    // assume there are no more pages
                    hasMorePages = false;
                    // may not exist on flatbed scanner but required for feeder
                    if (documentHandlingSelect != null)
                    {
                        // check for document feeder
                        if ((Convert.ToUInt32(documentHandlingSelect.get_Value()) & WIA_DPS_DOCUMENT_HANDLING_SELECT.FEEDER) != 0)
                        {
                            hasMorePages = ((Convert.ToUInt32(documentHandlingStatus.get_Value()) & WIA_DPS_DOCUMENT_HANDLING_STATUS.FEED_READY) != 0);
                        }
                    }
                }
                numbrPages -= 1;
                if (numbrPages > 0)
                    hasMorePages = true;
                else
                    hasMorePages = false;

            }
            return images;
        }
        /// <summary>
        /// Gets the list of available WIA devices.
        /// </summary>
        /// <returns></returns>
        public static List<string> GetDevices()
        {
            List<string> devices = new List<string>();
            WIA.DeviceManager manager = new WIA.DeviceManager();
            foreach (WIA.DeviceInfo info in manager.DeviceInfos)
            {
                devices.Add(info.DeviceID);
            }
            return devices;
        }
        private static void SetWIAProperty(WIA.IProperties properties,
               object propName, object propValue)
        {
            WIA.Property prop = properties.get_Item(ref propName);
            prop.set_Value(ref propValue);
        }
        private static void AdjustScannerSettings(IItem scannnerItem, int scanResolutionDPI, int scanStartLeftPixel, int scanStartTopPixel,
         int scanWidthPixels, int scanHeightPixels, int brightnessPercents, int contrastPercents, int colorMode)
        {
            const string WIA_SCAN_COLOR_MODE = "6146";
            const string WIA_HORIZONTAL_SCAN_RESOLUTION_DPI = "6147";
            const string WIA_VERTICAL_SCAN_RESOLUTION_DPI = "6148";
            const string WIA_HORIZONTAL_SCAN_START_PIXEL = "6149";
            const string WIA_VERTICAL_SCAN_START_PIXEL = "6150";
            const string WIA_HORIZONTAL_SCAN_SIZE_PIXELS = "6151";
            const string WIA_VERTICAL_SCAN_SIZE_PIXELS = "6152";
            const string WIA_SCAN_BRIGHTNESS_PERCENTS = "6154";
            const string WIA_SCAN_CONTRAST_PERCENTS = "6155";

            SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_RESOLUTION_DPI, scanResolutionDPI);
            SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_RESOLUTION_DPI, scanResolutionDPI);
            SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_START_PIXEL, scanStartLeftPixel);
            SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_START_PIXEL, scanStartTopPixel);
            SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_SIZE_PIXELS, scanWidthPixels);
            SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_SIZE_PIXELS, scanHeightPixels);
            SetWIAProperty(scannnerItem.Properties, WIA_SCAN_BRIGHTNESS_PERCENTS, brightnessPercents);
            SetWIAProperty(scannnerItem.Properties, WIA_SCAN_CONTRAST_PERCENTS, contrastPercents);
            SetWIAProperty(scannnerItem.Properties, WIA_SCAN_COLOR_MODE, colorMode);
        }

    }
}

Then see below the .cs file of the form named Form1

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;


namespace TESTSCAN
{
    public partial class Form1 : Form
    {
        int cropX, cropY, cropWidth, cropHeight;
        //here rectangle border pen color=red and size=2;
        Pen borderpen = new Pen(Color.Red, 2);
        Image _orgImage;
        Bitmap crop;
        List<string> devices;
        //fill the rectangle color =white
        SolidBrush rectbrush = new SolidBrush(Color.FromArgb(100, Color.White));
        int pages;
        int currentPage = 0;
        public Form1()
        {
            InitializeComponent();
            IsSaved = false;
        }
        List<Image> images;
        private string f_path;
        private string doc_no;
        private bool savedOrNot = false;
        private List<string> fNames;
        public List<string> fileNames
        {
            get { return fNames; }
            set { fNames = value; }
        }
        public String SavePath
        {
            get { return f_path; }
            set { f_path = value; }
        }
        public String DocNo
        {
            get { return doc_no; }
            set { doc_no = value; }
        }
        public bool IsSaved
        {
            get { return savedOrNot; }
            set { savedOrNot = value; }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            lblPath.Text = SavePath;
            lblDocNo.Text = DocNo;

            //get list of devices available
            devices = WIAScanner.GetDevices();

            foreach (string device in devices)
            {
                lbDevices.Items.Add(device);
            }
            //check if device is not available
            if (lbDevices.Items.Count != 0)
            {
                lbDevices.SelectedIndex = 0;

            }
        }

        private void btnPreview_Click(object sender, EventArgs e)
        {
            try
            {
                //get list of devices available

                if (lbDevices.Items.Count == 0)
                {
                    MessageBox.Show("You do not have any WIA devices.");

                }
                else
                {
                    //get images from scanner
                    pages =3;
                    images = WIAScanner.Scan((string)lbDevices.SelectedItem, pages);
                    pages = images.Count;
                    if (images != null)
                    {
                        foreach (Image image in images)
                        {
                            pic_scan.Image = images[0];
                            pic_scan.Show();
                            pic_scan.SizeMode = PictureBoxSizeMode.StretchImage;
                            _orgImage = images[0];
                            crop = new Bitmap(images[0]);
                            btnOriginal.Enabled = true;
                            btnSave.Enabled = true;
                            currentPage = 0;
                            //pic_scan.Image = image;
                            //pic_scan.Show();
                            //pic_scan.SizeMode = PictureBoxSizeMode.StretchImage;
                            //_orgImage = image;
                            //crop = new Bitmap(image);
                            //btnOriginal.Enabled = true;
                            //btnSave.Enabled = true;
                        }
                    }
                }


            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message);
            }

        }


        List<string> sss = new List<string>();
        private void btnSave_Click(object sender, EventArgs e)
        {
            try
            {
                if (crop != null)
                {
                    SavePath = @"D:\NAJEEB\scanned images\";
                    DocNo = "4444";
                    string currentFName = DocNo + Convert.ToString(currentPage + 1) + ".jpeg";
                    crop.Save(SavePath + currentFName, ImageFormat.Jpeg);
                    sss.Add(currentFName);
                    MessageBox.Show("Document Saved Successfully");
                    IsSaved = true;
                    currentPage += 1;
                    if (currentPage < (pages))
                    {
                        pic_scan.Image = images[currentPage];
                        pic_scan.Show();
                        pic_scan.SizeMode = PictureBoxSizeMode.StretchImage;
                        _orgImage = images[currentPage];
                        crop = new Bitmap(images[currentPage]);
                        btnOriginal.Enabled = true;
                        btnSave.Enabled = true;

                    }
                    else
                    { btnSave.Enabled =false;
                    fileNames = sss;
                    }
                }

            }
            catch (Exception exc)
            {
                IsSaved = false;
                MessageBox.Show(exc.Message);
            }
        }

        private void btnOriginal_Click(object sender, EventArgs e)
        {
            if (_orgImage != null)
            {
                crop = new Bitmap(_orgImage);
                pic_scan.Image = _orgImage;
                pic_scan.SizeMode = PictureBoxSizeMode.StretchImage;
                pic_scan.Refresh();
            }
        }

        private void pic_scan_MouseDown(object sender, MouseEventArgs e)
        {
            try
            {
                if (e.Button == MouseButtons.Left)//here i have use mouse click left button only
                {
                    pic_scan.Refresh();
                    cropX = e.X;
                    cropY = e.Y;
                    Cursor = Cursors.Cross;
                }
                pic_scan.Refresh();
            }
            catch { }
        }

        private void pic_scan_MouseMove(object sender, MouseEventArgs e)
        {
            try
            {
                if (pic_scan.Image == null)
                    return;

                if (e.Button == MouseButtons.Left)//here i have use mouse click left button only
                {
                    pic_scan.Refresh();
                    cropWidth = e.X - cropX;
                    cropHeight = e.Y - cropY;
                }
                pic_scan.Refresh();
            }
            catch { }
        }

        private void pic_scan_MouseUp(object sender, MouseEventArgs e)
        {
            try
            {
                Cursor = Cursors.Default;
                if (cropWidth < 1)
                {
                    return;
                }
                Rectangle rect = new Rectangle(cropX, cropY, cropWidth, cropHeight);
                Bitmap bit = new Bitmap(pic_scan.Image, pic_scan.Width, pic_scan.Height);
                crop = new Bitmap(cropWidth, cropHeight);
                Graphics gfx = Graphics.FromImage(crop);
                gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;//here add  System.Drawing.Drawing2D namespace;
                gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;//here add  System.Drawing.Drawing2D namespace;
                gfx.CompositingQuality = CompositingQuality.HighQuality;//here add  System.Drawing.Drawing2D namespace;
                gfx.DrawImage(bit, 0, 0, rect, GraphicsUnit.Pixel);

            }
            catch { }
        }

        private void pic_scan_Paint(object sender, PaintEventArgs e)
        {
            Rectangle rect = new Rectangle(cropX, cropY, cropWidth, cropHeight);
            Graphics gfx = e.Graphics;
            gfx.DrawRectangle(borderpen, rect);
            gfx.FillRectangle(rectbrush, rect);
        }
    }
}

Short description of the working flow:

  1. On form load event the available scanner devices adding to drop down list. The user can select any device from this drop down.
  2. On Preview button click, the document is scanned to application and is shown in the picture box.
  3. There is another picture box in the form for keeping the cropped image. If you select any area from the picture in the picture box then you will feel the cropping effect. The cropped image keep in the second picture box placed behind the first picture box.
  4. On save button click the image save into the specified location in the disk. This location we can configure from the application. See the code.