Java TWAIN with Dynamic .NET TWAIN and jni4net

Dynamic .NET TWAIN is one of the excellent TWAIN-compliant SDKs for document scanning and image capture. In this tutorial, I would like to share how to create a bridge to convert .NET TWAIN C# methods to Java methods in order to help Java developers easily implement Java TWAIN scanner software.

How to Make Demo Work

  1. Download Dynamic .NET TWAIN
  2. Download and learn jni4net to understand how JVM and CLR work together
  3. Correctly configure the paths of JAVA_HOME and  C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe in the system environment
  4. Unzip the sample code, and launch JavaTwain.sln to build JavaTwain.dll
  5. Copy bin and lib folders from jni4net package to your project directory
  6. Run generateProxies.cmd
  7. Run run.cmd

Java TWAIN

How to Make a Java TWAIN Scanner App based on .NET TWAIN with jni4net

To invoke the Dynamic .NET TWAIN instance, we need to create a bridge in C#.

  1. Add DynamicDotNetTWAIN.dll as a reference.TWAIN reference
  2. Create a class named DotNetScanner, and initialize the .NET TWAIN component in the constructor.
            public DotNetScanner()
            {
                // initialize TWAIN Component
                try
                {
                    dynamicDotNetTwain = new Dynamsoft.DotNet.TWAIN.DynamicDotNetTwain();
                    dynamicDotNetTwain.OnPostAllTransfers += new Dynamsoft.DotNet.TWAIN.Delegate.OnPostAllTransfersHandler(this.dynamicDotNetTwain_OnPostAllTransfers);
                    dynamicDotNetTwain.MaxImagesInBuffer = 64;
                    dynamicDotNetTwain.IfAppendImage = true;
                    dynamicDotNetTwain.IfThrowException = true;
                    dynamicDotNetTwain.IfShowUI = false;
                    dynamicDotNetTwain.IfThrowException = true;
                    dynamicDotNetTwain.ScanInNewProcess = true;
                }
                catch 
                {
                    MessageBox.Show(dynamicDotNetTwain.ErrorString);
                }
    
            }
  3. Create interfaces for data transmission between JVM and CLR.
        public interface IJavaProxy
        {
            bool AcquireImage(int iIndex);
            String[] GetSources();
            bool RegisterListener(INativeProxy proxy);
            void CloseSource();
        }
    
        public interface INativeProxy
        {
            bool Notify(String message, String value);
        }
  4. Capture an image and notify the Java layer to load it.
            public bool AcquireImage(int iIndex)
            {
                try
                {
                    //dynamicDotNetTwain.CloseSource();
                    bool success = dynamicDotNetTwain.SelectSourceByIndex(Convert.ToInt16(iIndex));
                    dynamicDotNetTwain.OpenSource();
                    dynamicDotNetTwain.AcquireImage();
                }
                catch (Dynamsoft.DotNet.TWAIN.TwainException exp)
                {
                    String errorstr = "";
                    errorstr += "Error " + exp.Code + "\r\n" + "Description: " + exp.Message + "\r\nPosition: " + exp.TargetSite + "\r\nHelp: " + exp.HelpLink + "\r\n";
                    MessageBox.Show(errorstr);
                }
                catch (Exception exp)
                {
                    String errorstr = "";
                    errorstr += "ErrorMessage: " + exp.Message + "\r\n";
                    MessageBox.Show(errorstr);
                }
    
                return true;
            }
    
            private void dynamicDotNetTwain_OnPostAllTransfers()
            {
                //MessageBox.Show("dynamicDotNetTwain_OnPostAllTransfers");
    
                if (dynamicDotNetTwain.MaxImagesInBuffer < 1)
                {
                    return;
                }
    
                Image img = dynamicDotNetTwain.GetImage(0);
                img = resizeImage(img, new Size(480, 640));
                img.Save("twain.png");
    
                if (listener != null)
                {
                    listener.Notify("data ready", "twain.png");
                }
            }

To make the C# interfaces work in Java:

  1. Load the JavaTwain.j4n.dll which is generated by proxygen.exe.
            private void initTWAIN() {
    		try {
    			Bridge.init();
    			Bridge.LoadAndRegisterAssemblyFrom(new java.io.File("JavaTwain.j4n.dll"));
    		}
    		catch (Exception e) {
                e.printStackTrace();
            }
    
    		mScanner = new DotNetScanner();
    		mScanner.RegisterListener(this);
    	}
  2. Create Swing UI to interact with the C# bridge.
       public ScanDocuments() {
            super(new BorderLayout());
    		initTWAIN();
    
            //Create a file chooser
            mFileChooser = new JFileChooser();
            FileNameExtensionFilter filter = new FileNameExtensionFilter(
                    ".png", "png");
            mFileChooser.setFileFilter(filter);
            mLoad = new JButton("Load");
            mLoad.addActionListener(this);
    
            mScan = new JButton("Scan");
            mScan.addActionListener(this);
    
    		// get sources
    		mSources = mScanner.GetSources();
    
    		if (mSources != null) {
    			mSourceList = new JComboBox(mSources);
    		}
    		else {
    			mSourceList = new JComboBox(new String[]{"N/A"});
    		}
            mSourceList.setSelectedIndex(0);
    
            // button panel
            JPanel buttonPanel = new JPanel(); 
    		buttonPanel.add(mSourceList);
    		buttonPanel.add(mScan);
    		buttonPanel.add(mLoad);
            add(buttonPanel, BorderLayout.PAGE_START);
    
            // image panel
    		JPanel imageViewer = new JPanel();
    		mImage = new JLabel();
    		mImage.setSize(480, 640);
    		imageViewer.add(mImage);
    		add(imageViewer, BorderLayout.CENTER);
        }

Try it yourself and have fun with Dynamic .NET TWAIN in Java.

Source Code

https://github.com/dynamsoft-dnt/JavaTwain