Usage: import pyfprint

Signed-off-by: Lukas Sandström <[EMAIL PROTECTED]>
---

Hi.

I've written a SWIG wrapper around libfprint for Python.

Hope this is useful for someone. It isn't very extensivly tested,
but the basics should work.

It doesn't raise proper exceptions for the various errors that can occur,
but I'll get around to implementing that sometime soon. 

Documentation i severely lacking right now, but it follows libfprint pretty
closely.

/Lukas

 Makefile.am              |    6 +
 autogen.sh               |    4 +-
 configure.ac             |   18 ++-
 m4/ac_pkg_swig.m4        |  125 ++++++++++++++
 m4/ac_python_devel.m4    |  268 +++++++++++++++++++++++++++++
 m4/swig_python.m4        |   67 ++++++++
 pyfprint/Makefile.am     |   12 ++
 pyfprint/pyfprint.py     |  423 ++++++++++++++++++++++++++++++++++++++++++++++
 pyfprint/pyfprint_swig.i |  176 +++++++++++++++++++
 9 files changed, 1095 insertions(+), 4 deletions(-)
 create mode 100644 m4/ac_pkg_swig.m4
 create mode 100644 m4/ac_python_devel.m4
 create mode 100644 m4/swig_python.m4
 create mode 100644 pyfprint/Makefile.am
 create mode 100644 pyfprint/pyfprint.py
 create mode 100644 pyfprint/pyfprint_swig.i

diff --git a/Makefile.am b/Makefile.am
index 4c63a15..f9b7365 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,10 +4,16 @@ DISTCLEANFILES = ChangeLog libfprint.pc
 
 SUBDIRS = libfprint doc
 
+ACLOCAL_AMFLAGS = -I m4
+
 if BUILD_EXAMPLES
 SUBDIRS += examples
 endif
 
+if PYFPRINT
+SUBDIRS += pyfprint
+endif
+
 pkgconfigdir=$(libdir)/pkgconfig
 pkgconfig_DATA=libfprint.pc
 
diff --git a/autogen.sh b/autogen.sh
index 5499285..374db0f 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,8 +1,8 @@
 #!/bin/sh
 libtoolize --copy --force || exit 1
-aclocal || exit 1
+aclocal -I m4 || exit 1
 autoheader || exit 1
-autoconf || exit 1
+autoconf -I m4 || exit 1
 automake -a -c || exit 1
 ./configure --enable-maintainer-mode --enable-examples-build \
        --enable-x11-examples-build --enable-debug-log $*
diff --git a/configure.ac b/configure.ac
index fb128cd..8abbb43 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,6 +3,8 @@ AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR([libfprint/core.c])
 AM_CONFIG_HEADER([config.h])
 
+dnl AC_DISABLE_STATIC
+
 AC_PREREQ([2.50])
 AC_PROG_CC
 AC_PROG_LIBTOOL
@@ -34,6 +36,18 @@ PKG_CHECK_MODULES(IMAGEMAGICK, "ImageMagick")
 AC_SUBST(IMAGEMAGICK_CFLAGS)
 AC_SUBST(IMAGEMAGICK_LIBS)
 
+AC_ARG_ENABLE([pyfprint], [AS_HELP_STRING([--enable-python],
+       [enable python wrapper (default n)])],
+       [pyfprint=$enableval],
+       [pyfprint='no'])
+AM_CONDITIONAL([PYFPRINT], [test "x$pyfprint" != "xno"])
+
+if test "x$pyfprint" != "xno"; then
+       AM_PATH_PYTHON(2.3)
+       AC_PROG_SWIG(1.3.21)
+       SWIG_PYTHON
+fi
+
 # Examples build
 AC_ARG_ENABLE([examples-build], [AS_HELP_STRING([--enable-examples-build],
        [build example applications (default n)])],
@@ -91,9 +105,9 @@ fi
 
 
 AC_DEFINE([API_EXPORTED], [__attribute__((visibility("default")))], [Default 
visibility])
-AM_CFLAGS="-std=gnu99 -fgnu89-inline -Wall -Wundef -Wunused 
-Wstrict-prototypes -Werror-implicit-function-declaration -Wno-pointer-sign 
-Wshadow"
+AM_CFLAGS="-std=gnu99 -Wall -Wundef -Wunused -Wstrict-prototypes 
-Werror-implicit-function-declaration -Wno-pointer-sign -Wshadow"
 AC_SUBST(AM_CFLAGS)
 
-AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] 
[examples/Makefile] [doc/Makefile])
+AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] 
[examples/Makefile] [doc/Makefile] [pyfprint/Makefile])
 AC_OUTPUT
 
diff --git a/m4/ac_pkg_swig.m4 b/m4/ac_pkg_swig.m4
new file mode 100644
index 0000000..040eba8
--- /dev/null
+++ b/m4/ac_pkg_swig.m4
@@ -0,0 +1,125 @@
+##### http://autoconf-archive.cryp.to/ac_pkg_swig.html
+#
+# SYNOPSIS
+#
+#   AC_PROG_SWIG([major.minor.micro])
+#
+# DESCRIPTION
+#
+#   This macro searches for a SWIG installation on your system. If
+#   found you should call SWIG via $(SWIG). You can use the optional
+#   first argument to check if the version of the available SWIG is
+#   greater than or equal to the value of the argument. It should have
+#   the format: N[.N[.N]] (N is a number between 0 and 999. Only the
+#   first N is mandatory.)
+#
+#   If the version argument is given (e.g. 1.3.17), AC_PROG_SWIG checks
+#   that the swig package is this version number or higher.
+#
+#   In configure.in, use as:
+#
+#     AC_PROG_SWIG(1.3.17)
+#     SWIG_ENABLE_CXX
+#     SWIG_MULTI_MODULE_SUPPORT
+#     SWIG_PYTHON
+#
+# LAST MODIFICATION
+#
+#   2006-10-22
+#
+# COPYLEFT
+#
+#   Copyright (c) 2006 Sebastian Huber <[EMAIL PROTECTED]>
+#   Copyright (c) 2006 Alan W. Irwin <[EMAIL PROTECTED]>
+#   Copyright (c) 2006 Rafael Laboissiere <[EMAIL PROTECTED]>
+#   Copyright (c) 2006 Andrew Collier <[EMAIL PROTECTED]>
+#
+#   This program is free software; you can redistribute it and/or
+#   modify it under the terms of the GNU General Public License as
+#   published by the Free Software Foundation; either version 2 of the
+#   License, or (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#   General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+#   02111-1307, USA.
+#
+#   As a special exception, the respective Autoconf Macro's copyright
+#   owner gives unlimited permission to copy, distribute and modify the
+#   configure scripts that are the output of Autoconf when processing
+#   the Macro. You need not follow the terms of the GNU General Public
+#   License when using or distributing such scripts, even though
+#   portions of the text of the Macro appear in them. The GNU General
+#   Public License (GPL) does govern all other use of the material that
+#   constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the
+#   Autoconf Macro released by the Autoconf Macro Archive. When you
+#   make and distribute a modified version of the Autoconf Macro, you
+#   may extend this special exception to the GPL to apply to your
+#   modified version as well.
+
+AC_DEFUN([AC_PROG_SWIG],[
+        AC_PATH_PROG([SWIG],[swig])
+        if test -z "$SWIG" ; then
+                AC_MSG_WARN([cannot find 'swig' program. You should look at 
http://www.swig.org])
+                SWIG='echo "Error: SWIG is not installed. You should look at 
http://www.swig.org"; ; false'
+        elif test -n "$1" ; then
+                AC_MSG_CHECKING([for SWIG version])
+                [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 
's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`]
+                AC_MSG_RESULT([$swig_version])
+                if test -n "$swig_version" ; then
+                        # Calculate the required version number components
+                        [required=$1]
+                        [required_major=`echo $required | sed 's/[^0-9].*//'`]
+                        if test -z "$required_major" ; then
+                                [required_major=0]
+                        fi
+                        [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+                        [required_minor=`echo $required | sed 's/[^0-9].*//'`]
+                        if test -z "$required_minor" ; then
+                                [required_minor=0]
+                        fi
+                        [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+                        [required_patch=`echo $required | sed 's/[^0-9].*//'`]
+                        if test -z "$required_patch" ; then
+                                [required_patch=0]
+                        fi
+                        # Calculate the available version number components
+                        [available=$swig_version]
+                        [available_major=`echo $available | sed 
's/[^0-9].*//'`]
+                        if test -z "$available_major" ; then
+                                [available_major=0]
+                        fi
+                        [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+                        [available_minor=`echo $available | sed 
's/[^0-9].*//'`]
+                        if test -z "$available_minor" ; then
+                                [available_minor=0]
+                        fi
+                        [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+                        [available_patch=`echo $available | sed 
's/[^0-9].*//'`]
+                        if test -z "$available_patch" ; then
+                                [available_patch=0]
+                        fi
+                        if test $available_major -ne $required_major \
+                                -o $available_minor -ne $required_minor \
+                                -o $available_patch -lt $required_patch ; then
+                                AC_MSG_WARN([SWIG version >= $1 is required.  
You have $swig_version.  You should look at http://www.swig.org])
+                                SWIG='echo "Error: SWIG version >= $1 is 
required.  You have '"$swig_version"'.  You should look at http://www.swig.org"; 
; false'
+                        else
+                                AC_MSG_NOTICE([SWIG executable is '$SWIG'])
+                                SWIG_LIB=`$SWIG -swiglib`
+                                AC_MSG_NOTICE([SWIG library directory is 
'$SWIG_LIB'])
+                        fi
+                else
+                        AC_MSG_WARN([cannot determine SWIG version])
+                        SWIG='echo "Error: Cannot determine SWIG version.  You 
should look at http://www.swig.org"; ; false'
+                fi
+        fi
+        AC_SUBST([SWIG_LIB])
+])
diff --git a/m4/ac_python_devel.m4 b/m4/ac_python_devel.m4
new file mode 100644
index 0000000..ed00e0b
--- /dev/null
+++ b/m4/ac_python_devel.m4
@@ -0,0 +1,268 @@
+##### http://autoconf-archive.cryp.to/ac_python_devel.html
+#
+# SYNOPSIS
+#
+#   AC_PYTHON_DEVEL([version])
+#
+# DESCRIPTION
+#
+#   Note: Defines as a precious variable "PYTHON_VERSION". Don't
+#   override it in your configure.ac.
+#
+#   This macro checks for Python and tries to get the include path to
+#   'Python.h'. It provides the $(PYTHON_CPPFLAGS) and
+#   $(PYTHON_LDFLAGS) output variables. It also exports
+#   $(PYTHON_EXTRA_LIBS) and $(PYTHON_EXTRA_LDFLAGS) for embedding
+#   Python in your code.
+#
+#   You can search for some particular version of Python by passing a
+#   parameter to this macro, for example ">= '2.3.1'", or "== '2.4'".
+#   Please note that you *have* to pass also an operator along with the
+#   version to match, and pay special attention to the single quotes
+#   surrounding the version number. Don't use "PYTHON_VERSION" for
+#   this: that environment variable is declared as precious and thus
+#   reserved for the end-user.
+#
+#   This macro should work for all versions of Python >= 2.1.0. As an
+#   end user, you can disable the check for the python version by
+#   setting the PYTHON_NOVERSIONCHECK environment variable to something
+#   else than the empty string.
+#
+#   If you need to use this macro for an older Python version, please
+#   contact the authors. We're always open for feedback.
+#
+# LAST MODIFICATION
+#
+#   2007-07-31
+#
+# COPYLEFT
+#
+#   Copyright (c) 2007 Sebastian Huber <[EMAIL PROTECTED]>
+#   Copyright (c) 2007 Alan W. Irwin <[EMAIL PROTECTED]>
+#   Copyright (c) 2007 Rafael Laboissiere <[EMAIL PROTECTED]>
+#   Copyright (c) 2007 Andrew Collier <[EMAIL PROTECTED]>
+#   Copyright (c) 2007 Matteo Settenvini <[EMAIL PROTECTED]>
+#   Copyright (c) 2007 Horst Knorr <[EMAIL PROTECTED]>
+#
+#   This program is free software: you can redistribute it and/or
+#   modify it under the terms of the GNU General Public License as
+#   published by the Free Software Foundation, either version 3 of the
+#   License, or (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#   General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program. If not, see
+#   <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright
+#   owner gives unlimited permission to copy, distribute and modify the
+#   configure scripts that are the output of Autoconf when processing
+#   the Macro. You need not follow the terms of the GNU General Public
+#   License when using or distributing such scripts, even though
+#   portions of the text of the Macro appear in them. The GNU General
+#   Public License (GPL) does govern all other use of the material that
+#   constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the
+#   Autoconf Macro released by the Autoconf Macro Archive. When you
+#   make and distribute a modified version of the Autoconf Macro, you
+#   may extend this special exception to the GPL to apply to your
+#   modified version as well.
+
+AC_DEFUN([AC_PYTHON_DEVEL],[
+       #
+       # Allow the use of a (user set) custom python version
+       #
+       AC_ARG_VAR([PYTHON_VERSION],[The installed Python
+               version to use, for example '2.3'. This string
+               will be appended to the Python interpreter
+               canonical name.])
+
+       AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
+       if test -z "$PYTHON"; then
+          AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
+          PYTHON_VERSION=""
+       fi
+
+       #
+       # Check for a version of Python >= 2.1.0
+       #
+       AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
+       ac_supports_python_ver=`$PYTHON -c "import sys, string; \
+               ver = string.split(sys.version)[[0]]; \
+               print ver >= '2.1.0'"`
+       if test "$ac_supports_python_ver" != "True"; then
+               if test -z "$PYTHON_NOVERSIONCHECK"; then
+                       AC_MSG_RESULT([no])
+                       AC_MSG_FAILURE([
+This version of the AC@&[EMAIL PROTECTED] macro
+doesn't work properly with versions of Python before
+2.1.0. You may need to re-run configure, setting the
+variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG,
+PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
+Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
+to something else than an empty string.
+])
+               else
+                       AC_MSG_RESULT([skip at user request])
+               fi
+       else
+               AC_MSG_RESULT([yes])
+       fi
+
+       #
+       # if the macro parameter ``version'' is set, honour it
+       #
+       if test -n "$1"; then
+               AC_MSG_CHECKING([for a version of Python $1])
+               ac_supports_python_ver=`$PYTHON -c "import sys, string; \
+                       ver = string.split(sys.version)[[0]]; \
+                       print ver $1"`
+               if test "$ac_supports_python_ver" = "True"; then
+                  AC_MSG_RESULT([yes])
+               else
+                       AC_MSG_RESULT([no])
+                       AC_MSG_ERROR([this package requires Python $1.
+If you have it installed, but it isn't the default Python
+interpreter in your system path, please pass the PYTHON_VERSION
+variable to configure. See ``configure --help'' for reference.
+])
+                       PYTHON_VERSION=""
+               fi
+       fi
+
+       #
+       # Check if you have distutils, else fail
+       #
+       AC_MSG_CHECKING([for the distutils Python package])
+       ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+       if test -z "$ac_distutils_result"; then
+               AC_MSG_RESULT([yes])
+       else
+               AC_MSG_RESULT([no])
+               AC_MSG_ERROR([cannot import Python module "distutils".
+Please check your Python installation. The error was:
+$ac_distutils_result])
+               PYTHON_VERSION=""
+       fi
+
+       #
+       # Check for Python include path
+       #
+       AC_MSG_CHECKING([for Python include path])
+       if test -z "$PYTHON_CPPFLAGS"; then
+               python_path=`$PYTHON -c "import distutils.sysconfig; \
+                       print distutils.sysconfig.get_python_inc();"`
+               if test -n "${python_path}"; then
+                       python_path="-I$python_path"
+               fi
+               PYTHON_CPPFLAGS=$python_path
+       fi
+       AC_MSG_RESULT([$PYTHON_CPPFLAGS])
+       AC_SUBST([PYTHON_CPPFLAGS])
+
+       #
+       # Check for Python library path
+       #
+       AC_MSG_CHECKING([for Python library path])
+       if test -z "$PYTHON_LDFLAGS"; then
+               # (makes two attempts to ensure we've got a version number
+               # from the interpreter)
+               py_version=`$PYTHON -c "from distutils.sysconfig import *; \
+                       from string import join; \
+                       print join(get_config_vars('VERSION'))"`
+               if test "$py_version" == "[None]"; then
+                       if test -n "$PYTHON_VERSION"; then
+                               py_version=$PYTHON_VERSION
+                       else
+                               py_version=`$PYTHON -c "import sys; \
+                                       print sys.version[[:3]]"`
+                       fi
+               fi
+
+               PYTHON_LDFLAGS=`$PYTHON -c "from distutils.sysconfig import *; \
+                       from string import join; \
+                       print '-L' + get_python_lib(0,1), \
+                       '-lpython';"`$py_version
+       fi
+       AC_MSG_RESULT([$PYTHON_LDFLAGS])
+       AC_SUBST([PYTHON_LDFLAGS])
+
+       #
+       # Check for site packages
+       #
+       AC_MSG_CHECKING([for Python site-packages path])
+       if test -z "$PYTHON_SITE_PKG"; then
+               PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
+                       print distutils.sysconfig.get_python_lib(0,0);"`
+       fi
+       AC_MSG_RESULT([$PYTHON_SITE_PKG])
+       AC_SUBST([PYTHON_SITE_PKG])
+
+       #
+       # libraries which must be linked in when embedding
+       #
+       AC_MSG_CHECKING(python extra libraries)
+       if test -z "$PYTHON_EXTRA_LIBS"; then
+          PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
+                conf = distutils.sysconfig.get_config_var; \
+                print conf('LOCALMODLIBS'), conf('LIBS')"`
+       fi
+       AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
+       AC_SUBST(PYTHON_EXTRA_LIBS)
+
+       #
+       # linking flags needed when embedding
+       #
+       AC_MSG_CHECKING(python extra linking flags)
+       if test -z "$PYTHON_EXTRA_LDFLAGS"; then
+               PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
+                       conf = distutils.sysconfig.get_config_var; \
+                       print conf('LINKFORSHARED')"`
+       fi
+       AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
+       AC_SUBST(PYTHON_EXTRA_LDFLAGS)
+
+       #
+       # final check to see if everything compiles alright
+       #
+       AC_MSG_CHECKING([consistency of all components of python development 
environment])
+       AC_LANG_PUSH([C])
+       # save current global flags
+       LIBS="$ac_save_LIBS $PYTHON_LDFLAGS"
+       CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
+       AC_TRY_LINK([
+               #include <Python.h>
+       ],[
+               Py_Initialize();
+       ],[pythonexists=yes],[pythonexists=no])
+
+       AC_MSG_RESULT([$pythonexists])
+
+        if test ! "$pythonexists" = "yes"; then
+          AC_MSG_ERROR([
+  Could not link test program to Python. Maybe the main Python library has been
+  installed in some non-standard library path. If so, pass it to configure,
+  via the LDFLAGS environment variable.
+  Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
+  ============================================================================
+   ERROR!
+   You probably have to install the development version of the Python package
+   for your distribution.  The exact name of this package varies among them.
+  ============================================================================
+          ])
+         PYTHON_VERSION=""
+       fi
+       AC_LANG_POP
+       # turn back to default flags
+       CPPFLAGS="$ac_save_CPPFLAGS"
+       LIBS="$ac_save_LIBS"
+
+       #
+       # all done!
+       #
+])
diff --git a/m4/swig_python.m4 b/m4/swig_python.m4
new file mode 100644
index 0000000..0eccd59
--- /dev/null
+++ b/m4/swig_python.m4
@@ -0,0 +1,67 @@
+##### http://autoconf-archive.cryp.to/swig_python.html
+#
+# SYNOPSIS
+#
+#   SWIG_PYTHON([use-shadow-classes = {no, yes}])
+#
+# DESCRIPTION
+#
+#   Checks for Python and provides the $(SWIG_PYTHON_CPPFLAGS), and
+#   $(SWIG_PYTHON_OPT) output variables.
+#
+#   $(SWIG_PYTHON_OPT) contains all necessary SWIG options to generate
+#   code for Python. Shadow classes are enabled unless the value of the
+#   optional first argument is exactly 'no'. If you need multi module
+#   support (provided by the SWIG_MULTI_MODULE_SUPPORT macro) use
+#   $(SWIG_PYTHON_LIBS) to link against the appropriate library. It
+#   contains the SWIG Python runtime library that is needed by the type
+#   check system for example.
+#
+# LAST MODIFICATION
+#
+#   2006-10-22
+#
+# COPYLEFT
+#
+#   Copyright (c) 2006 Sebastian Huber <[EMAIL PROTECTED]>
+#   Copyright (c) 2006 Alan W. Irwin <[EMAIL PROTECTED]>
+#   Copyright (c) 2006 Rafael Laboissiere <[EMAIL PROTECTED]>
+#   Copyright (c) 2006 Andrew Collier <[EMAIL PROTECTED]>
+#
+#   This program is free software; you can redistribute it and/or
+#   modify it under the terms of the GNU General Public License as
+#   published by the Free Software Foundation; either version 2 of the
+#   License, or (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#   General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+#   02111-1307, USA.
+#
+#   As a special exception, the respective Autoconf Macro's copyright
+#   owner gives unlimited permission to copy, distribute and modify the
+#   configure scripts that are the output of Autoconf when processing
+#   the Macro. You need not follow the terms of the GNU General Public
+#   License when using or distributing such scripts, even though
+#   portions of the text of the Macro appear in them. The GNU General
+#   Public License (GPL) does govern all other use of the material that
+#   constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the
+#   Autoconf Macro released by the Autoconf Macro Archive. When you
+#   make and distribute a modified version of the Autoconf Macro, you
+#   may extend this special exception to the GPL to apply to your
+#   modified version as well.
+
+AC_DEFUN([SWIG_PYTHON],[
+        AC_REQUIRE([AC_PROG_SWIG])
+        AC_REQUIRE([AC_PYTHON_DEVEL])
+        test "x$1" != "xno" || swig_shadow=" -noproxy"
+        AC_SUBST([SWIG_PYTHON_OPT],[-python$swig_shadow])
+        AC_SUBST([SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS])
+])
diff --git a/pyfprint/Makefile.am b/pyfprint/Makefile.am
new file mode 100644
index 0000000..c6c767e
--- /dev/null
+++ b/pyfprint/Makefile.am
@@ -0,0 +1,12 @@
+BUILT_SOURCES = $(srcdir)/pyfprint_swig.c
+SWIG_SOURCES = pyfprint_swig.i
+
+pkgpython_PYTHON = pyfprint_swig.py pyfprint.py
+pkgpyexec_LTLIBRARIES = _pyfprint_swig.la
+_pyfprint_swig_la_SOURCES = $(srcdir)/pyfprint_swig.c $(SWIG_SOURCES)
+_pyfprint_swig_la_CPPFLAGS = $(SWIG_PYTHON_CPPFLAGS) -I$(top_srcdir)/libfprint
+_pyfprint_swig_la_LDFLAGS = -module
+_pyfprint_swig_la_LIBADD = ../libfprint/libfprint.la
+
+$(srcdir)/pyfprint_swig.c : $(SWIG_SOURCES)
+       $(SWIG) $(SWIG_PYTHON_OPT) -I$(top_srcdir)/libfprint -o $@ $<
diff --git a/pyfprint/pyfprint.py b/pyfprint/pyfprint.py
new file mode 100644
index 0000000..255f4db
--- /dev/null
+++ b/pyfprint/pyfprint.py
@@ -0,0 +1,423 @@
+
+import pyfprint_swig as pyf
+
+# TODO:
+#      exceptions, especially for RETRY_* errors
+#      constants for fingers
+#      tests
+#      documentation
+#      for x in y => map ?
+#      Image(img) for devices which don't support imaging? Is img NULL?
+
+_init_ok = False
+
+def _dbg(*arg):
+       print arg
+
+def fp_init():
+       _init_ok = (pyf.fp_init() == 0)
+       if not _init_ok:
+               raise "fprint initialization failed."
+
+def fp_exit():
+       pyf.fp_exit()
+       _init_ok = False
+
+Fingers = dict(
+       LEFT_THUMB = pyf.LEFT_THUMB,
+       LEFT_INDEX = pyf.LEFT_INDEX,
+       LEFT_MIDDLE = pyf.LEFT_MIDDLE,
+       LEFT_RING = pyf.LEFT_RING,
+       LEFT_LITTLE = pyf.LEFT_LITTLE,
+       RIGHT_THUMB = pyf.RIGHT_THUMB,
+       RIGHT_INDEX = pyf.RIGHT_INDEX,
+       RIGHT_MIDDLE = pyf.RIGHT_MIDDLE,
+       RIGHT_RING = pyf.RIGHT_RING,
+       RIGHT_LITTLE = pyf.RIGHT_LITTLE
+       )
+
+class Device:
+       def __init__(self, dev_ptr = None, dscv_ptr = None, DscvList = None):
+               self.dev = dev_ptr
+               self.dscv = dscv_ptr
+               self.DscvList = DscvList
+               if dscv_ptr and DscvList == None:
+                       raise "Programming error? Device contructed with dscv 
without DscvList."
+
+       def close(self):
+               if self.dev:
+                       pyf.fp_dev_close(self.dev)
+               self.dev = None
+
+       def open(self):
+               if self.dev:
+                       raise "Device already open"
+               self.dev = pyf.fp_dev_open(self.dscv)
+               if not self.dev:
+                       raise "device open failed"
+
+       def get_driver(self):
+               if self.dev:
+                       return Driver(pyf.fp_dev_get_driver(self.dev))
+               if self.dscv:
+                       return Driver(pyf.pf_dscv_dev_get_driver(self.dscv))
+
+       def get_devtype(self):
+               if self.dev:
+                       return pyf.fp_dev_get_devtype(self.dev)
+               if self.dscv:
+                       return pyf.fp_dscv_dev_get_devtype(self.dev)
+
+       def get_nr_enroll_stages(self):
+               if self.dev:
+                       return pyf.fp_dev_get_nr_enroll_stages(self.dev)
+               raise "Device not open"
+
+       def is_compatible(self, fprint):
+               if self.dev:
+                       if fprint.data_ptr:
+                               return pyf.fp_dev_supports_print_data(self.dev, 
fprint.data_ptr) == 1
+                       if fprint.dscv_ptr:
+                               return pyf.fp_dev_supports_dscv_print(self.dev, 
fprint.dscv_ptr) == 1
+                       raise "No print found"
+               if self.dscv:
+                       if fprint.data_ptr:
+                               return 
pyf.fp_dscv_dev_supports_print_data(self.dscv, fprint.data_ptr) == 1
+                       if fprint.dscv_ptr:
+                               return 
pyf.fp_dscv_dev_supports_dscv_print(self.dscv, fprint.dscv_ptr) == 1
+                       raise "No print found"
+               raise "No device found"
+
+       def get_supports_imaging(self):
+               if self.dev:
+                       return pyf.fp_dev_supports_imaging(self.dev) == 1
+               raise "Device not open"
+
+       def get_img_width(self):
+               if self.dev:
+                       return pyf.fp_dev_get_img_width(self.dev)
+               raise "Device not open"
+
+       def get_img_height(self):
+               if self.dev:
+                       return pyf.fp_dev_get_img_height(self.dev)
+               raise "Device not open"
+
+       def capture_image(self, wait_for_finger):
+               """FIXME: check that the dev supports imaging, or check 
-ENOTSUP"""
+               if not self.dev:
+                       raise "Device not open"
+
+               unconditional = 1
+               if wait_for_finger == True:
+                       unconditional = 0
+
+               (r, img) = pyf.fp_dev_img_capture(self.dev, unconditional)
+               if r != 0:
+                       raise "image_capture failed. error: " + r
+               return Image(img)
+
+       def enroll_finger(self):
+               if not self.dev:
+                       raise "Device not open"
+               (r, fprint, img) = pyf.fp_enroll_finger_img(self.dev)
+               if r < 0:
+                       raise "Internal I/O error while enrolling"
+               img = Image(img)
+               if r == pyf.FP_ENROLL_COMPLETE:
+                       _dbg("enroll complete")
+                       return (Fprint(data_ptr = fprint), img)
+               if r == pyf.FP_ENROLL_FAIL:
+                       print "Failed. Enrollmet process reset."
+               if r == pyf.FP_ENROLL_PASS:
+                       _dbg("enroll PASS")
+                       return (None, img)
+               if r == pyf.FP_ENROLL_RETRY:
+                       _dbg("enroll RETRY")
+                       pass
+               if r == pyf.FP_ENROLL_RETRY_TOO_SHORT:
+                       _dbg("enroll RETRY_SHORT")
+                       pass
+               if r == pyf.FP_ENROLL_RETRY_CENTER_FINGER:
+                       _dbg("enroll RETRY_CENTER")
+                       pass
+               if r == pyf.FP_ENROLL_RETRY_REMOVE_FINGER:
+                       _dbg("enroll RETRY_REMOVE")
+                       pass
+               return ("xxx", None)
+
+       def verify_finger(self, fprint):
+               if not self.dev:
+                       raise "Device not open"
+               (r, img) = pyf.fp_verify_finger_img(self.dev, 
fprint._get_print_data_ptr())
+               if r < 0:
+                       raise "verify error"
+               img = Image(img)
+               if r == pyf.FP_VERIFY_NO_MATCH:
+                       return (False, img)
+               if r == pyf.FP_VERIFY_MATCH:
+                       return (True, img)
+               if r == pyf.FP_VERIFY_RETRY:
+                       pass
+               if r == pyf.FP_VERIFY_RETRY_TOO_SHORT:
+                       pass
+               if r == pyf.FP_VERIFY_RETRY_CENTER_FINGER:
+                       pass
+               if r == pyf.FP_VERIFY_RETRY_REMOVE_FINGER:
+                       pass
+               return (None, None)
+
+       def supports_identification(self):
+               if not self.dev:
+                       raise "Device not open"
+               return pyf.fp_dev_supports_identification(self.dev) == 1
+
+       def identify_finger(self, fprints):
+               """Returns a tuple: (list_offset, Fprint, Image) if a match is 
found,
+               (None, None, Image) otherwise. Image might be NULL if the 
device doesn't
+               support imaging."""
+
+               if not self.dev:
+                       raise "Device not open"
+               gallery = pyf.pyfp_print_data_array(len(fprints))
+               for x in fprints:
+                       if not self.is_compatible(x):
+                               raise "can't verify uncompatible print"
+                       gallery.append(x._get_print_data_ptr())
+               (r, offset, img) = pyf.fp_identify_finger_img(self.dev, 
gallery.list)
+               if r < 0:
+                       raise "identification error"
+               img = Image(img)
+               if r == pyf.FP_VERIFY_NO_MATCH:
+                       return (None, None, img)
+               if r == pyf.FP_VERIFY_MATCH:
+                       return (offset, fprints[offset], img)
+               if r == pyf.FP_VERIFY_RETRY:
+                       pass
+               if r == pyf.FP_VERIFY_RETRY_TOO_SHORT:
+                       pass
+               if r == pyf.FP_VERIFY_RETRY_CENTER_FINGER:
+                       pass
+               if r == pyf.FP_VERIFY_RETRY_REMOVE_FINGER:
+                       pass
+               return None
+
+       def load_print_from_disk(self, finger):
+               if not self.dev:
+                       raise "Device not open"
+               (r, print_ptr) = pyf.fp_print_data_load(self.dev, finger)
+               if r != 0:
+                       raise "could not load print from disk"
+               return Fprint(data_ptr = print_ptr)
+
+       def delete_stored_finger(self, finger):
+               if not self.dev:
+                       raise "Device not open"
+               r = pyf.fp_print_data_delete(self.dev, finger)
+               if r != 0:
+                       raise "delete failed"
+
+class Minutia(pyf.fp_minutia):
+       def __init__(self, minutia_ptr, img):
+               self.img = img
+               self.ptr = minutia_ptr
+               pyf.fp_minutia.__init__(self, minutia_ptr)
+
+class Image:
+       def __init__(self, img_ptr, bin = False):
+               self.img = img_ptr
+               self.bin = bin
+               self.std = False
+               self.minutiae = None
+
+       def __del__(self):
+               if self.img:
+                       pyf.fp_img_free(self.img)
+
+       def get_height(self):
+               return pyf.fp_img_get_height(self.img)
+       def get_width(self):
+               return pyf.fp_img_get_width(self.img)
+
+       def get_data(self):
+               return pyf.pyfp_get_img_data(self.img)
+
+       def save_to_file(self, path):
+               r = pyf.fp_img_save_to_file(self.img, path)
+               if r != 0:
+                       raise "Save failed"
+
+       def standardize(self):
+               pyf.fp_img_standardize(self.img)
+               self.std = True
+
+       def binarize(self):
+               if self.bin:
+                       return
+               if not self.std:
+                       self.standardize()
+               i = pyf.fp_img_binarize(self.img)
+               if i == None:
+                       raise "Binarize failed"
+               return Image(img_ptr = i, bin = True)
+
+       def get_minutiae(self):
+               if self.minutiae:
+                       return self.minutiae
+               if self.bin:
+                       raise "Cannot find minutiae in binarized image"
+               if not self.std:
+                       self.standardize()
+               (min_list, nr) = pyf.fp_img_get_minutiae(self.img)
+               l = []
+               for n in range(nr):
+                       l.append(Minutia(img = self, minutia_ptr = 
pyf.pyfp_deref_minutiae(min_list, n)))
+               self.minutiae = l
+               return l
+
+class Driver:
+       def __init__(self, swig_drv_ptr):
+               self.drv = swig_drv_ptr
+
+       def __del__(self):
+               #FIXME: free drv?
+               pass
+
+       def get_name(self):
+               return pyf.fp_driver_get_name(self.drv)
+
+       def get_full_name(self):
+               return pyf.fp_driver_get_full_name(self.drv)
+
+       def get_driver_id(self):
+               return pyf.fp_driver_get_driver_id(self.drv)
+
+class Fprint:
+       def __init__(self, serial_data = None, data_ptr = None, dscv_ptr = 
None, DscvList = None):
+               # data_ptr is a SWIG pointer to a struct pf_print_data
+               # dscv_ptr is a SWIG pointer to a struct pf_dscv_print
+               # DscvList is a class instance used to free the allocated 
pf_dscv_print's
+               #          with pf_dscv_prints_free when they're all unused.
+               # serial_data is a string as returned by get_data()
+
+               self.data_ptr = data_ptr
+               self.dscv_ptr = dscv_ptr
+               self.DscvList = DscvList
+
+               if serial_data:
+                       self.data_ptr = pyf.fp_print_data_from_data(serial_data)
+                       return
+
+               if dscv_ptr != None and DscvList == None:
+                       raise "Programming error: Fprint constructed with 
dscv_prt with DscvList == None"
+
+       def __del__(self):
+               if self.data_ptr:
+                       pyf.fp_print_data_free(self.data_ptr)
+               # The dscv_ptr is freed when all the dscv prints have been 
garbage collected
+
+       def _get_print_data_ptr(self):
+               if not self.data_ptr:
+                       self._data_from_dscv(self)
+               return self.data_ptr
+
+       def get_driver_id(self):
+               if self.data_ptr:
+                       return pyf.fp_print_data_get_driver_id(self.data_ptr)
+               elif self.dscv_ptr:
+                       return pyf.fp_dscv_print_get_driver_id(self.dscv_ptr)
+               raise "no print"
+
+       def get_devtype(self):
+               if self.data_ptr:
+                       return pyf.fp_print_data_get_devtype(self.data_ptr)
+               elif self.dscv_ptr:
+                       return pyf.fp_dscv_print_get_devtype(self.dscv_ptr)
+               raise "no print"
+
+       def get_finger(self):
+               if not self.dscv_ptr:
+                       raise "get_finger needs a discovered print"
+               return pyf.fp_dscv_print_get_finger(self.dscv_ptr)
+
+       def delete_from_disk(self):
+               if not self.dscv_ptr:
+                       raise "delete needs a discovered print"
+               return pyf.fp_dscv_print_delete(self.dscv_ptr)
+
+       def save_to_disk(self, finger):
+               r = pyf.fp_print_data_save(self.data_ptr, finger)
+               if r != 0:
+                       raise "save failed"
+
+       def _data_from_dscv(self):
+               if self.data_ptr:
+                       return
+               if not self.dscv_ptr:
+                       raise "no print"
+               (r, ptr) = pyf.fp_print_data_from_dscv_print(self.dscv_ptr)
+               if r != 0:
+                       raise "print data from dscv failed"
+               self.data_ptr = ptr
+
+       def get_data(self):
+               if not self.data_ptr:
+                       raise "no print"
+               s = pyf.pyfp_print_get_data(self.data_ptr)
+               if not len(s):
+                       raise "serialization failed"
+               return s
+
+class DiscoveredPrints(list):
+       def __init__(self, dscv_devs_list):
+               self.ptr = dscv_devs_list
+               i = 0
+               while True:
+                       x = pyf.pyfp_deref_dscv_print_ptr(dscv_devs_list, i)
+                       if x == None:
+                               break
+                       self.append(Fprint(dscv_ptr = x, DscvList = self))
+                       i = i + 1
+       def __del__(self):
+               pyf.pf_dscv_prints_free(self.ptr)
+
+def discover_prints():
+       if not _init_ok:
+               fp_init()
+
+       prints = pyf.fp_discover_prints()
+
+       if not prints:
+               print "Print discovery failed"
+       return DiscoveredPrints(prints)
+
+
+class DiscoveredDevices(list):
+       def __init__(self, dscv_devs_list):
+               self.swig_list_ptr = dscv_devs_list
+               i = 0
+               while True:
+                       x = pyf.pyfp_deref_dscv_dev_ptr(dscv_devs_list, i)
+                       if x == None:
+                               break
+                       self.append(Device(dscv_ptr = x, DscvList = self))
+                       i = i + 1
+
+       def __del__(self):
+               pyf.fp_dscv_devs_free(self.swig_list_ptr)
+
+       def find_compatible(self, fprint):
+               for n in self:
+                       if n.is_compatible(fprint):
+                               return n
+               return None
+
+def discover_devices():
+       if not _init_ok:
+               fp_init()
+
+       devs = pyf.fp_discover_devs()
+
+       if not devs:
+               raise "Device discovery failed"
+       return DiscoveredDevices(devs)
\ No newline at end of file
diff --git a/pyfprint/pyfprint_swig.i b/pyfprint/pyfprint_swig.i
new file mode 100644
index 0000000..6bfc2b2
--- /dev/null
+++ b/pyfprint/pyfprint_swig.i
@@ -0,0 +1,176 @@
+%module pyfprint_swig
+%{
+#include <fprint.h>
+#include <errno.h>
+%}
+
+%feature("autodoc", "1");
+
+%include <typemaps.i>
+%include <cdata.i>
+%include <carrays.i>
+%include <cstring.i>
+
+%nodefaultctor;
+
+/* fp_dev_img_capture,  fp_enroll_finger_img, fp_verify_finger_img, 
fp_identify_finger_img */
+%typemap(argout) struct fp_img ** {
+    PyObject *o;
+    o = SWIG_NewPointerObj(*$1, $*1_descriptor, 1);
+    $result = SWIG_AppendOutput($result, o);
+    /* FIXME: is a PY_DECREF(o) needed here ?*/
+}
+%typemap(in, numinputs=0) struct fp_img **(struct fp_img *img) {
+    $1 = &img;
+}
+
+/* fp_enroll_finger_img */
+%typemap(argout) struct fp_print_data **print_data = struct fp_img **;
+%typemap(in, numinputs=0) struct fp_print_data **print_data(struct 
fp_print_data *data) {
+    $1 = &data;
+}
+
+/* fp_print_data_load, fp_print_data_from_dscv_print */
+%apply struct fp_print_data **print_data { struct fp_print_data **data };
+
+/* fp_identify_finger */
+%apply unsigned long *OUTPUT { size_t *match_offset };
+
+/* fp_print_data_from_data */
+%apply (char *STRING, int LENGTH) { (unsigned char *buf, size_t buflen) };
+
+/* fp_img_get_minutiae */
+%apply int *OUTPUT { int *nr_minutiae };
+
+/* Tell SWIG that we're freeing the pointers */
+%delobject fp_dscv_devs_free;
+%delobject fp_img_free;
+%delobject fp_print_data_free;
+%delobject fp_dscv_prints_free;
+%delobject fp_dev_close;
+%delobject pyfp_free_print_data_array;
+
+/* Tell SWIG that we're allocating new objects */
+%newobject pyfp_alloc_print_data_array;
+%newobject fp_dev_open;
+
+/* Image.get_minutiae() */
+%inline %{
+struct fp_minutia * pyfp_deref_minutiae(struct fp_minutia **ptr, int i)
+{
+       return ptr[i];
+}
+
+%}
+/* The struct needs to be redefined as const, otherwise swig will generate 
_set_ methods for the members. */
+struct fp_minutia {
+       const int x;
+       const int y;
+       const int ex;
+       const int ey;
+       const int direction;
+       const double reliability;
+       const int type;
+       const int appearing;
+       const int feature_id;
+       int * const nbrs;
+       int * const ridge_counts;
+       const int num_nbrs;
+
+       %extend {
+               /* A constructor that accepts pre-allocated structs */
+               fp_minutia(struct fp_minutia *ptr)
+               {
+                       return ptr;
+               }
+               ~fp_minutia()
+               {
+                       /* Don't free() fp_minutia *. They are free'd together 
with the fp_img. */ ;
+               }
+       };
+};
+%ignore fp_minutia;
+
+/* Needed to get correct output from
+   fp_dscv_print_get_driver_id and fp_dev_get_devtype */
+typedef unsigned int uint32_t;
+/* fp_driver_get_driver_id, fp_dscv_print_get_driver_id, 
fp_print_data_get_driver_id*/
+typedef unsigned short int uint16_t;
+
+/* Fprint.get_data() */
+%cstring_output_allocate_size(char **print_data, int *len, free(*($1)));
+%inline %{
+void pyfp_print_get_data(char **print_data, int *len, struct fp_print_data 
*print)
+{
+       *len = fp_print_data_get_data(print, (unsigned char**)print_data);
+}
+%}
+%ignore fp_print_data_get_data;
+
+/* Img.get_data() */
+%cstring_output_allocate_size(char **img_data, int *len, "");
+%inline %{
+void pyfp_img_get_data(char **img_data, int *len, struct fp_img *img)
+{
+       *img_data = fp_img_get_data(img);
+       *len = fp_img_get_width(img) * fp_img_get_height(img);
+}
+%}
+%ignore fp_img_get_data;
+
+
+%include "fprint.h"
+
+
+/* Device.identify_finger() */
+%inline %{
+struct pyfp_print_data_array {
+       size_t size;
+       size_t used;
+       struct fp_print_data * list[0];
+};
+%}
+%extend pyfp_print_data_array {
+       pyfp_print_data_array(size_t size)
+       {
+               struct pyfp_print_data_array *x;
+               x = calloc(1, sizeof(struct pyfp_print_data_array) +
+                               sizeof(struct fp_print_data *) * (size + 1)); 
/* +1 for NULL termination */
+               x->size = size;
+               return x;
+       }
+       ~pyfp_print_data_array()
+       {
+               free($self);
+       }
+       void append(struct fp_print_data *print)
+       {
+               if ($self->size <= $self->used) {
+                       PyErr_SetString(PyExc_OverflowError, "programming 
error: pyfp_print_data_array list overflow");
+                       return;
+               }
+               $self->list[$self->used] = print;
+               $self->used++;
+       }
+       struct fp_print_data ** pyfp_print_data_array_list_get()
+       {
+               return $self->list;
+       }
+};
+
+%inline %{
+
+/* DiscoveredDevices.__init__() */
+struct fp_dscv_dev * pyfp_deref_dscv_dev_ptr (struct fp_dscv_dev **ptr, int i)
+{
+       return ptr[i];
+}
+
+/* class DiscoveredPrints(list): */
+struct fp_dscv_print * pyfp_deref_dscv_print_ptr(struct fp_dscv_print **ptr, 
int i)
+{
+       return ptr[i];
+}
+
+
+%}
-- 
1.5.3.7

_______________________________________________
fprint mailing list
[email protected]
http://lists.reactivated.net/mailman/listinfo/fprint

Reply via email to