package uk.co.mmscomputing.device.twain;
import java.util.Vector;
import java.io.*;
import java.awt.image.*;
import java.lang.ref.*;
import java.lang.reflect.*;
import uk.co.mmscomputing.util.*;
import uk.co.mmscomputing.concurrent.*;
public class jtwain implements TwainConstants{
static private boolean installed;
static private Thread nativeThread; // make sure to call DSM_ENTRY only from this native thread!
static private TwainSourceManager sourceManager; // applet: this object will live as long as browser window is open
static private WeakReference scanner; // applet: set to new object if applet reloads
static private Object syncScanner = new Object(); // synchronization object
static private native void nstart(); // run native event loop
// 32 | 64 bit system
static private native int ngetPtrSize(); // 32 or 64 bit system
static private int ptrSize = 0; // in byte
static int getPtrSize(){ return ptrSize;}
// java -> native
static private native void ntrigger(Object caller,int cmd); // call cbexecute from native thread
// TwainSourceManager -> native
static private native TwainSourceManager ngetSourceManager()throws TwainException; // initialize native library
static private native int ncallSourceManager(int dg,int dat,int msg,byte[] data)throws TwainException;
// TwainSource -> native
static native byte[] ngetContainer(byte[] capBuf); // copy container pointed to by TW_CAPABILITY.hContainer into byte buffer
static native void nsetContainer(byte[] capbuf, byte[] conbytes); // copy byte buffer into native memory container and setup capability buffer
static native void nfreeContainer(byte[] capbuf); // free native container memory
static private native int ncallSource(byte[] source,int dg,int dat,int msg,byte[] data)throws TwainException;
static private native long ngetCallBackMethod(); // get twain 2.0 callback method
static private long ptrCallback = 0; // pointer to twain 2.0 callback method
static long getCallBackMethod(){return ptrCallback;}
// for twain native transfer mode
static private native Object ntransferImage(long imagePtr); // returns BufferedImage object
// for twain memory transfer mode
static native void nnew(byte[] twImageMemXfer,int size); // allocate native memory block
static native int ncopy(byte[] buf,byte[] twImageMemXfer,int size); // copy native memory block into java byte array
// static native byte[] ncopy(byte[] twImageMemXfer,int size); // copy native memory block into java byte array
static native void ndelete(byte[] twImageMemXfer); // free native memory block
static public boolean isInstalled(){return installed;}
static int getINT16(byte[] buf,int off){return (buf[off++]&0x00FF)|(buf[off]<<8);}
static void setINT16(byte[] buf,int off,int i){buf[off++]=(byte)i;buf[off++]=(byte)(i>>8);}
static int getINT32(byte[] buf,int off){return (buf[off++]&0x00FF)|((buf[off++]&0x00FF)<<8)|((buf[off++]&0x00FF)<<16)|(buf[off]<<24);}
static void setINT32(byte[] buf,int off,int i){buf[off++]=(byte)i;buf[off++]=(byte)(i>>8);buf[off++]=(byte)(i>>16);buf[off++]=(byte)(i>>24);}
static long getINT64(byte[] buf,int off){
return (buf[off++]&0x00FFL) |((buf[off++]&0x00FFL)<< 8)|((buf[off++]&0x00FFL)<<16)|((buf[off++]&0x00FFL)<<24)
|((buf[off++]&0x00FFL)<<32)|((buf[off++]&0x00FFL)<<40)|((buf[off++]&0x00FFL)<<48)|((buf[off++]&0x00FFL)<<56);
}
static void setINT64(byte[] buf,int off,long i){
buf[off++]=(byte) i ;buf[off++]=(byte)(i>> 8);buf[off++]=(byte)(i>>16);buf[off++]=(byte)(i>>24);
buf[off++]=(byte)(i>>32);buf[off++]=(byte)(i>>40);buf[off++]=(byte)(i>>48);buf[off++]=(byte)(i>>56);
}
static long getPtr(byte[] buf,int off){
if(ptrSize==8){return getINT64(buf,off);
}else{ return getINT32(buf,off);
}
}
static int setPtr(byte[] buf,int off,long i){
if(ptrSize==8){setINT64(buf,off, i);return 8;
}else{ setINT32(buf,off,(int)i);return 4;
}
}
static double getFIX32(byte[] buf,int off){
int whole =((buf[off++]&0x00FF)|(buf[off++]<<8));
int frac =((buf[off++]&0x00FF)|((buf[off]&0x00FF)<<8));
return ((double)whole)+((double)frac)/65536.0; // [1] 4-76
}
static void setFIX32(byte[] buf,int off,double d){
int value =(int)(d*65536.0+((d<0)?(-0.5):0.5)); // [1] 4-78 + fix for negative numbers
setINT16(buf,off ,value>>16); // whole
setINT16(buf,off+2,value&0x0000FFFF); // frac
}
static void setString(byte[] buf,int off,String s){
System.arraycopy(s.getBytes(),0,buf,0,s.length());
}
static int callSourceManager(int dg,int dat,int msg,byte[] data)throws TwainIOException{
if(Thread.currentThread()!=nativeThread){
throw new TwainIOException("Call twain source manager only from within native thread.");
}
try{
return ncallSourceManager(dg,dat,msg,data);
}catch(TwainException te){ // disaster: source exception in native code
installed=false;throw te; // disable jtwain, no calls to twain anymore
}
}
static int callSource(byte[] source,int dg,int dat,int msg,byte[] data)throws TwainIOException{
if(Thread.currentThread()!=nativeThread){
throw new TwainIOException("Call twain source only from within native thread.");
}
try{
return ncallSource(source,dg,dat,msg,data);
}catch(TwainException tioe){ // disaster: source exception in native code
installed=false;throw tioe; // disable jtwain, no calls to twain anymore
}
}
static public TwainSourceManager getSourceManager()throws TwainIOException{
try{ // wait until native thread is up and running
while((sourceManager==null)&&installed){Thread.currentThread().sleep(100);}
}catch(Exception e){}
if(!installed){
throw new TwainIOException("Cannot load Twain Source Manager.");
}
return sourceManager;
}
static public TwainSource getSource()throws TwainIOException{
return getSourceManager().getSource();
}
static private void trigger(Object caller,int cmd){
try{ // wait until native thread is up and running
while((sourceManager==null)&&installed){Thread.currentThread().sleep(100);}
if(installed){ntrigger(caller,cmd);}
}catch(Exception e){}
}
static public void callSetSource(Object caller){trigger(caller,0);} // caller.setSource(sourceManager.getSource());
static private TwainScanner getScanner(){
return (scanner!=null)?(TwainScanner)scanner.get():null;
}
static private boolean setScanner(TwainScanner s)throws TwainIOException{ // use of weak reference to TwainScanner object
synchronized(syncScanner){
TwainScanner cs = getScanner();
if((cs==null)||!cs.isBusy()){
scanner=new WeakReference<TwainScanner>(s); // allows garbage collection in applets
return true;
}
throw new TwainIOException(jtwain.class.getName()+".setScanner\n\tScanner is still busy.");
}
}
static public void select(TwainScanner sc)throws TwainIOException{
if(setScanner(sc)){
TwainSourceManager sm=getSourceManager(); // jtwain might not be up and running yet
sm.getSource().checkState(3);
trigger(sc,1);
}
}
// used by TwainScanner
static /* public */ void getIdentities(TwainScanner sc, Vector list)throws TwainIOException{
if(setScanner(sc)){
TwainSourceManager sm=getSourceManager(); // jtwain might not be up and running yet
sm.getSource().checkState(3); // System.err.println("select: try "+name);
Semaphore s = new Semaphore(0,true); // need to wait for native thread to retrieve list of identities
Object[] parameter = {list,s}; // need to transport two objects via one parameter to cbexecute
trigger(parameter,2); // call cbexecute case 2
try{
s.tryAcquire(3000,TimeUnit.MILLISECONDS);
if(list.isEmpty() && (parameter[1]!=null)){
throw new TwainIOException(jtwain.class.getName()+".getIdentities\n\tCould not retrieve device names. Request timed out.");
}
}catch(InterruptedException ie){
throw new TwainIOException(jtwain.class.getName()+".getIdentities\n\tCould not retrieve device names. Request was interrupted.");
}
}
}
static public void select(TwainScanner sc, String name)throws TwainIOException{
if(setScanner(sc)){
TwainSourceManager sm=getSourceManager(); // jtwain might not be up and running yet
sm.getSource().checkState(3); // System.err.println("select: try "+name);
trigger(name,3);
}
}
static public void acquire(TwainScanner sc)throws TwainIOException{
if(setScanner(sc)){
TwainSourceManager sm=getSourceManager(); // jtwain might not be up and running yet
sm.getSource().checkState(3);
trigger(sc,4);
}
}
static public void setCancel(TwainScanner sc, boolean c)throws TwainIOException{
getSourceManager().getSource().setCancel(c);
}
static private Method getMethod(Class clazz,String name,Class[] paramTypes)throws NoSuchMethodException{
if(clazz==null){ throw new NoSuchMethodException();}
try{
return clazz.getDeclaredMethod(name,paramTypes);
}catch(NoSuchMethodException nsme){
return getMethod(clazz.getSuperclass(),name,paramTypes);
}
}
static private void cbexecute(Object obj,int cmd){ // callback function cpp -> java; in windows thread;
TwainSource source;
try{
switch(cmd){
case 0: // an object wants access to current twain source
Class clazz =obj.getClass();
try{ // introspection
source=sourceManager.getSource();
Method method=getMethod(clazz,"setSource",new Class[]{TwainSource.class});
method.invoke(obj,new Object[]{source}); // caller.setSource(source);
}catch(Throwable e){e.printStackTrace();} // cannot find method: programming error
break;
case 1: // select
sourceManager.selectSource(); // System.out.println(sourceManager.getSource());
break;
case 2: // getIdentities
Object[] parameter = (Object[])obj; // parameter list from getIdentities
Vector<TwainIdentity> list = (Vector<TwainIdentity>)parameter[0];
Semaphore s = (Semaphore)parameter[1];
sourceManager.getIdentities(list);
parameter[1] = null;
s.release();
break;
case 3: // select name
String name = (String)obj; // System.out.println("cbexecute: try "+name);
sourceManager.selectSource(name); // System.err.println(sourceManager.getSource());
break;
case 4: // acquire
source=sourceManager.openSource();
try{
source.enable();
}finally{
source.close();
}
break;
}
}catch(Throwable e){
signalException(e.getMessage());
e.printStackTrace();
}
}
static private int cbhandleGetMessage(long msg){ // callback function cpp -> java; windows thread;
try{
return sourceManager.getSource().handleGetMessage(msg);
}catch(Throwable e){
signalException(e.getMessage());
e.printStackTrace();
return TWRC_NOTDSEVENT;
}
}
// callback function cpp -> java; twain 2.0;
static private int cbmethod20(byte[] corigin,byte[] cdest,int DG,int DAT,int MSG,long dataptr){
try{
TwainIdentity origin = new TwainIdentity(sourceManager,corigin); // this should always be the twain data source
// System.out.println(origin.toString());
// TwainIdentity dest = new TwainIdentity(sourceManager,cdest); // this should always be the twain data source manager
// System.out.println(dest.toString());
TwainSource source = sourceManager.getSource();
if(origin.getId()!=source.getId()){ // we are only waiting for callbacks from our datasource, so validate the originator
return TWRC_FAILURE;
}
return source.callback(DG,DAT,MSG,dataptr);
}catch(Throwable e){
signalException(e.getMessage());
e.printStackTrace();
return TWRC_FAILURE;
}
}
static void signalStateChange(TwainSource source){
TwainScanner scanner=getScanner();
if(scanner!=null){
scanner.setState(source);
}
}
static void signalException(String msg){
TwainScanner scanner=getScanner();
if(scanner!=null){
scanner.signalException(msg);
}
}
static void negotiateCapabilities(TwainSource source){
TwainScanner scanner=getScanner();
if(scanner!=null){
scanner.negotiateCapabilities(source);
}
}
static void transferNativeImage(long dibHandle){
BufferedImage image=(BufferedImage)ntransferImage(dibHandle);
if(image!=null){
TwainScanner scanner=getScanner();
if(scanner!=null){
scanner.setImage(image);
}
}
}
static void transferFileImage(File file){
if(file!=null){
TwainScanner scanner=getScanner();
if(scanner!=null){
scanner.setImage(file);
}
}
}
static void transferMemoryBuffer(TwainTransfer.MemoryTransfer.Info info){
TwainScanner scanner=getScanner();
if(scanner!=null){
scanner.setImageBuffer(info);
}
}
static{
// win : load library 'jtwain.dll'
// installed = JarLib.load(jtwain.class,"jtwain");
installed = TwainNativeLoadStrategySingleton.getInstance().getNativeLoadStrategy().load( jtwain.class,"jtwain");
if(installed){
ptrSize = ngetPtrSize();
ptrCallback = ngetCallBackMethod();
nativeThread=new Thread(){
public void run(){
try{
sourceManager=ngetSourceManager();
if(sourceManager!=null){
nstart();
}
}catch(Throwable e){
System.err.println("uk.co.mmscomputing.device.twain.jtwain\t\n"+e.getMessage());
System.err.println(e);
e.printStackTrace();
}
installed=false;
}
};
nativeThread.setDaemon(true);
nativeThread.start();
}
}
}
// [1] Twain Spec 1.9
// [2] Twain Spec 2.0