[PATCH] Very basic SWIG Perl wrapper and supporting automake/autoconf scripts

Tim Brody tdb2 at ecs.soton.ac.uk
Wed Nov 3 05:13:23 PDT 2010


---
 configure.ac          |   53 ++++++++
 swig/Makefile.am      |    6 +
 swig/generic.mk       |    7 +
 swig/perl/Makefile.am |   33 +++++
 swig/perl/util.i.in   |  338 +++++++++++++++++++++++++++++++++++++++++++++++++
 swig/poppler.i        |  105 +++++++++++++++
 6 files changed, 542 insertions(+), 0 deletions(-)
 create mode 100644 swig/Makefile.am
 create mode 100644 swig/generic.mk
 create mode 100644 swig/perl/Makefile.am
 create mode 100644 swig/perl/util.i.in
 create mode 100644 swig/poppler.i

diff --git a/configure.ac b/configure.ac
index 8e3c5ac..3eac3d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -540,6 +540,56 @@ fi
 
 AM_CONDITIONAL(BUILD_POPPLER_CPP, test "x$enable_poppler_cpp" = "xyes")
 
+dnl
+dnl SWIG bindings
+dnl
+
+BINDINGS=
+
+AC_ARG_ENABLE(poppler-swig,
+              AC_HELP_STRING([--disable-poppler-swig],
+                             [Don't compile poppler swig bindings.]),
+              enable_poppler_swig=$enableval,
+              enable_poppler_swig="yes")
+if test x$enable_poppler_swig = xyes; then
+  AC_PATH_PROGS(SWIG, ["${SWIG-swig}"], [])
+  if test -z "$SWIG" ; then
+    enable_poppler_swig=no
+  else
+    AC_PATH_PROGS(PERL, ["${PERL-perl}"], [])
+    if test -n "$PERL" ; then
+      AC_ARG_VAR(PERL_INC, [Directory to include XS headers])
+      if test -z "$PERL_INC" ; then
+        PERL_INC=`$PERL -MConfig -e 'print $Config{archlibexp}, "/CORE";'`
+      fi
+      AC_SUBST(PERL_INC)
+
+      AC_ARG_VAR(PERL_LIB, [Directory to install perl files into])
+      if test -z "$PERL_LIB" ; then
+        [PERL_LIB=`$PERL -MConfig -e 'print $Config{sitearch};'`];
+      fi
+      AC_SUBST(PERL_LIB)
+
+      AC_ARG_VAR(PERL_SO, [for perl module extension])
+      if test -z "$PERL_SO" ; then
+        [PERL_SO=`$PERL -MConfig -e 'print ".", $Config{'dlext'};'`];
+      fi
+      AC_SUBST(PERL_SO)
+
+      if test -z "$PERL_POPPLER_VERSION" ; then
+        [PERL_POPPLER_VERSION=$POPPLER_VERSION];
+      fi
+      AC_SUBST(PERL_POPPLER_VERSION)
+
+      BINDINGS="$BINDINGS perl"
+    fi
+  fi
+fi
+
+AC_SUBST(BINDINGS)
+
+AM_CONDITIONAL(BUILD_POPPLER_SWIG, test "x$enable_poppler_swig" = "xyes")
+
 
 AC_ARG_ENABLE(gtk-test,
               AC_HELP_STRING([--disable-gtk-test],
@@ -654,6 +704,8 @@ qt4/demos/Makefile
 cpp/Makefile
 cpp/poppler-version.h
 cpp/tests/Makefile
+swig/Makefile
+swig/perl/Makefile swig/perl/util.i
 poppler.pc
 poppler-uninstalled.pc
 poppler-cairo.pc
@@ -689,6 +741,7 @@ echo "  use zlib:           $enable_zlib"
 echo "  use libcurl:        $enable_libcurl"
 echo "  use libopenjpeg:    $enable_libopenjpeg"
 echo "  use cms:            $enable_cms"
+echo "  swig bindings:      $BINDINGS"
 echo "  command line utils: $enable_utils"
 echo ""
 
diff --git a/swig/Makefile.am b/swig/Makefile.am
new file mode 100644
index 0000000..7d71150
--- /dev/null
+++ b/swig/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/goo \
+	-I$(top_srcdir)/poppler
+
+SUBDIRS = @BINDINGS@
diff --git a/swig/generic.mk b/swig/generic.mk
new file mode 100644
index 0000000..0a7c191
--- /dev/null
+++ b/swig/generic.mk
@@ -0,0 +1,7 @@
+# General Makefile settings for all language-bindings
+
+SWIG_mainsource = \
+	$(srcdir)/../poppler.i
+
+SWIG_sources = \
+	$(SWIG_mainsource)
diff --git a/swig/perl/Makefile.am b/swig/perl/Makefile.am
new file mode 100644
index 0000000..f983613
--- /dev/null
+++ b/swig/perl/Makefile.am
@@ -0,0 +1,33 @@
+
+
+include ../generic.mk
+
+BUILT_SOURCES = poppler_wrap.cc
+
+EXTRA_DIST = util.i $(BUILT_SOURCES)
+
+perllibdir = $(PERL_LIB)/auto/Poppler
+perllib_LTLIBRARIES = Poppler.la
+
+perlmoddir = $(PERL_LIB)
+perlmod_DATA = Poppler.pm
+
+#perlPODdir = $(PERL_LIB)
+#perlPOD_DATA = lib/*
+
+AM_CPPFLAGS = -I$(PERL_INC)
+Poppler_la_SOURCES = poppler_wrap.cc
+Poppler_la_LIBADD = \
+	$(top_builddir)/poppler/libpoppler.la \
+	$(PERL_LIBS)
+Poppler_la_LDFLAGS = \
+	@create_shared_lib@ \
+	-avoid-version \
+	-module \
+	-shrext $(PERL_SO) \
+	$(NO_UNDEFINED)
+
+poppler_wrap.cc Poppler.pm: 
+	$(SWIG) $(INCLUDES) $(SWIG_FLAGS) -c++ \
+		-perl -module Poppler -proxy -const \
+		-o poppler_wrap.cc $(SWIG_mainsource)
diff --git a/swig/perl/util.i.in b/swig/perl/util.i.in
new file mode 100644
index 0000000..7059f22
--- /dev/null
+++ b/swig/perl/util.i.in
@@ -0,0 +1,338 @@
+/* type-checking for overloaded methods */
+%typecheck(SWIG_TYPECHECK_POINTER) BaseStream *, OutStream * {
+    $1 = sv_isio(aTHX_ $input);
+}
+%typecheck(SWIG_TYPECHECK_POINTER) PDFRectangle * {
+    $1 = SvTYPE(SvRV($input)) == SVt_PVHV;
+}
+%typecheck(SWIG_TYPECHECK_POINTER) AnnotColor * {
+    $1 = SvTYPE(SvRV($input)) == SVt_PVAV;
+}
+/* any scalar can be stringified or true/false */
+%typecheck(SWIG_TYPECHECK_STRING) GooString *, GBool {
+    $1 = 1;
+}
+
+/* GooStrings look like a duplication of std::string to me */
+%typemap(in) GooString * {
+    STRLEN len;
+    $1 = $input != &PL_sv_undef ?
+        new GooString(SvPV($input,len),len) :
+        NULL;
+}
+%typemap(out) GooString * {
+    if (argvi >= items) {
+        EXTEND(sp,1);
+    }
+    $result = sv_newmortal();
+    if( $1 != NULL )
+    {
+        sv_setpvn($result, $1->getCString(), $1->getLength());
+        gfree($1);
+    }
+    argvi++;
+}
+
+/* should've used std::bool */
+%typemap(in) GBool {
+    $1 = SvTRUE($input);
+}
+%typemap(out) GBool {
+    if (argvi >= items) {
+        EXTEND(sp,1);
+    }
+    $result = sv_newmortal();
+    sv_setiv($result, $1);
+    argvi++;
+}
+%typemap(in) Unicode, CID, CharCode {
+    const char *pat = "W";
+    STRLEN len;
+    const char *s = SvPV($input,len);
+    dSP;
+    unpackstring(pat,pat+1,s,s+len,0);
+    $1 = SvUV(ST(0));
+    PUTBACK;
+    SPAGAIN;
+}
+%typemap(out) Unicode, CID, CharCode {
+    if (argvi >= items) {
+        EXTEND(sp,1);
+    }
+    const char *pat = "U";
+    SV *svp[1];
+    svp[0] = sv_2mortal(newSVUV($1));
+    $result = sv_newmortal();
+    packlist($result,pat,pat+1,svp,svp+1);
+    argvi++;
+}
+
+%typemap(in) PDFRectangle * {
+    HV *hv = (HV *) SvRV($input);
+
+    double rect[4];
+    const char *idx[4] = {
+        "x1", "y1", "x2", "y2"
+    };
+
+    for(int i = 0; i < 4; ++i)
+    {
+        SV **sv = hv_fetch(hv,idx[i],strlen(idx[i]),NULL);
+        if( sv == NULL )
+        {
+            croak("Expected '%s' in hash reference", idx[i]);
+        }
+        rect[i] = SvNV(*sv);
+    }
+
+    $1 = new PDFRectangle(rect[0],rect[1],rect[2],rect[3]);
+}
+%typemap(out) PDFRectangle * {
+    if (argvi >= items) {
+        EXTEND(sp,1);
+    }
+    HV *hv = newHV();
+    hv_stores(hv, "x1", newSVnv($1->x1));
+    hv_stores(hv, "y1", newSVnv($1->y1));
+    hv_stores(hv, "x2", newSVnv($1->x2));
+    hv_stores(hv, "y2", newSVnv($1->y2));
+    $result = sv_2mortal(newRV_noinc((SV *) hv));
+    argvi++;
+}
+%typemap(in) AnnotColor * {
+    AV *av = (AV *) SvRV($input);
+    double color[4];
+
+    for(int i = 0; i <= av_len(av) && i < 4; ++i)
+    {
+        SV **svp = av_fetch(av,i,NULL);
+        color[i] = svp != NULL ? SvNV(*svp) : 0;
+    }
+
+    switch(av_len(av)+1)
+    {
+        case 0:
+            $1 = new AnnotColor();
+            break;
+        case 1:
+            $1 = new AnnotColor(color[0]);
+            break;
+        case 3:
+            $1 = new AnnotColor(color[0],color[1],color[2]);
+            break;
+        case 4:
+            $1 = new AnnotColor(color[0],color[1],color[2],color[3]);
+            break;
+        default:
+            croak("Expected 0, 1, 3 or 4 elements in array");
+    }
+}
+%typemap(out) AnnotColor * {
+    if (argvi >= items) {
+        EXTEND(sp,1);
+    }
+    AV *av = newAV();
+    const double *values = $1->getValues();
+    for(int i = 0; i < $1->getSpace(); ++i)
+    {
+        av_push(av, newSVnv(values[i]));
+    }
+    $result = sv_2mortal(newRV_noinc((SV *) av));
+    argvi++;
+}
+/* when reading PDFDoc stores the stream and frees it */
+%typemap(in) BaseStream * (Object obj) {
+    PerlIO *fh = IoIFP(sv_2io($input));
+
+    obj.initNull();
+    $1 = new PerlInStream(fh, 0, gFalse, 0, &obj);
+}
+/* when writing we need to free the output stream */
+%typemap(in) OutStream * {
+    PerlIO *fh = IoOFP(sv_2io($input));
+
+    $1 = new PerlOutStream(fh);
+}
+%typemap(freearg) OutStream * {
+    free($1);
+}
+
+%{
+/* Copied from Win32::Job */
+static int sv_isio(pTHX_ SV *sv)
+{
+	IO *io;
+	GV *gv;
+	STRLEN n_a;
+
+	switch (SvTYPE(sv)) {
+	case SVt_PVIO:
+		io = (IO*)sv;
+		return 1;
+	case SVt_PVGV:
+		gv = (GV*)sv;
+		io = GvIO(gv);
+		if (!io)
+			return 0;
+		return 1;
+	default:
+		if (!SvOK(sv))
+			return 0;
+		if (SvROK(sv))
+			return sv_isio(aTHX_ SvRV(sv));
+		gv = gv_fetchpv(SvPV(sv,n_a), FALSE, SVt_PVIO);
+		if (gv)
+			return 1;
+		else
+			return 0;
+	}
+	return 0;
+}
+
+class PerlOutStream: public OutStream {
+	public:
+		PerlOutStream(PerlIO *f): OutStream() {
+			f_ = f;
+		};
+		void close() {};
+		int getPos() { return PerlIO_tell(f_); };
+		void put(char c) { PerlIO_putc(f_, c); };
+		void printf(const char *format, ...) {
+			va_list argptr;
+			va_start(argptr, format);
+			PerlIO_vprintf(f_, format, argptr);
+			va_end(argptr);
+		};
+	private:
+		PerlIO *f_;
+};
+
+class PerlInStream: public BaseStream {
+	public:
+		PerlInStream(PerlIO *f, Guint start, GBool limited, Guint length, Object *dict): BaseStream(dict) {
+			f_ = f;
+			start_ = static_cast<Off_t>(start);
+			limited_ = limited;
+			length_ = static_cast<Off_t>(length);
+			savePos_ = 0;
+			saved_ = false;
+			total_ = 0;
+		};
+		~PerlInStream() {
+			close();
+		};
+		StreamKind getKind() { return strFile; };
+		void reset() {
+			savePos_ = PerlIO_tell(f_);
+			PerlIO_seek(f_, start_, SEEK_SET);
+			saved_ = true;
+		};
+		void close() {
+			if(saved_) {
+				PerlIO_seek(f_, savePos_, SEEK_SET);
+				saved_ = false;
+			}
+		};
+		int getChar() {
+			if(limited_ && PerlIO_tell(f_) >= start_ + length_)
+				return EOF;
+			++total_;
+			return PerlIO_getc(f_);
+		};
+		int lookChar() {
+			int c = getChar();
+			PerlIO_ungetc(f_, c);
+			return c == EOF ? EOF : c & 0xff;
+		};
+		int getPos() {
+			return (int)PerlIO_tell(f_);
+		};
+		void setPos(Guint pos, int dir = 0) {
+			Off_t size;
+			if( dir >= 0 ) {
+				if(PerlIO_seek(f_, pos, SEEK_SET) != 0)
+					croak("Error in file seek");
+			}
+			else {
+				if(PerlIO_seek(f_, 0, SEEK_END) != 0)
+					croak("Error in file seek");
+				size = static_cast<Guint>(PerlIO_tell(f_));
+				if( pos > size )
+					pos = static_cast<Guint>(size);
+				if(PerlIO_seek(f_, -(Off_t)pos, SEEK_END) != 0)
+					croak("Error in file seek");
+			}
+		};
+		Stream *makeSubStream(Guint start, GBool limited, Guint length, Object *dict) {
+			return new PerlInStream(f_, start, limited, length, dict);
+		};
+		int getUnfilteredChar() { return getChar(); };
+		void unfilteredReset() { reset(); };
+		Guint getStart() { return static_cast<Guint>(start_); };
+		void moveStart(int delta) {
+			start_ += delta;
+		};
+	private:
+		virtual GBool hasGetChars() { return true; }
+		virtual int getChars(int nchars, Guchar *buffer) {
+			return PerlIO_read(f_, buffer, nchars);
+		}
+
+		PerlIO *f_;
+		Off_t start_;
+		GBool limited_;
+		Off_t length_;
+		bool saved_;
+		Off_t savePos_;
+		Off_t total_;
+};
+%}
+
+%perlcode %{
+
+package Poppler;
+
+our $VERSION = v at POPPLER_VERSION@;
+
+=pod
+
+=head1 NAME
+
+Poppler - poppler PDF API
+
+=head1 SYNOPSIS
+
+    use Poppler;
+
+    $doc = Poppler::PDFDoc->new( "foo.pdf" );
+
+    print $doc->getNumPages(), "\n";
+
+    $txtdev = Poppler::TextOutputDev->new( "foo.txt",
+        1, # physical layout
+        0, # raw order
+        0, # append
+    );
+
+    $doc->displayPages( $txtdev,
+        1, # from
+        $doc->getNumPages, # to
+        72, # h-DPI
+        72, # v-DPI
+        0, # rotation
+        1, # use media box
+        0, # crop
+        0, # printing
+    );
+
+=head1 DESCRIPTION
+
+These bindings are for the 'libpoppler' library.
+
+=head1 SEE ALSO
+
+L<Poppler>, http://poppler.freedesktop.org/
+
+=cut
+
+%}
diff --git a/swig/poppler.i b/swig/poppler.i
new file mode 100644
index 0000000..4cd4fb2
--- /dev/null
+++ b/swig/poppler.i
@@ -0,0 +1,105 @@
+/*
+ * Poppler SWIG wrapper
+ *
+ * Copyright 2010 Tim Brody <tdb2 at ecs.soton.ac.uk
+ *
+ * Released under the same license as Poppler (GPL 2 or later as of writing).
+ *
+ */
+
+%module "poppler"
+%{
+#include <poppler/GlobalParams.h>
+#include <poppler/OutputDev.h>
+#include <poppler/PDFDoc.h>
+#include <poppler/TextOutputDev.h>
+#include <poppler/DateInfo.h>
+%}
+
+/* these symbols seem to be missing on FC13, according to PERL_DL_NOLAZY */
+%ignore TextWord::primaryCmp(TextWord *word);
+%ignore AnnotAppearance;
+
+/* PDFRectangle are turned into hashes */
+%ignore PDFRectangle;
+/* SWIG can't cope with the weird const Time struct */
+%ignore AnnotMovie;
+/* AnnotColor are turned into arrays */
+%ignore AnnotColor;
+
+/* Ignore 'Unicode' character methods */
+%ignore Unicode;
+%ignore TextWord::getChar(int idx);
+%ignore TextPage::findText(Unicode *s, int len,
+            GBool startAtTop, GBool stopAtBottom,
+            GBool startAtLast, GBool stopAtLast,
+            GBool caseSensitive, GBool backward,
+            double *xMin, double *yMin,
+            double *xMax, double *yMax);
+%ignore TextOutputDev::findText(Unicode *s, int len,
+            GBool startAtTop, GBool stopAtBottom,
+            GBool startAtLast, GBool stopAtLast,
+            GBool caseSensitive, GBool backward,
+            double *xMin, double *yMin,
+            double *xMax, double *yMax);
+
+/* objects and methods only used by higher-level methods */
+%ignore TextPage::addChar(GfxState *state, double x, double y,
+            double dx, double dy,
+            CharCode c, int nBytes, Unicode *u, int uLen);
+%ignore TextOutputDev::drawChar(GfxState *state, double x, double y,
+            double dx, double dy,
+            double originX, double originY,
+            CharCode c, int nBytes, Unicode *u, int uLen);
+%ignore ActualText;
+%ignore TextPage::coalesce;
+%ignore TextBlock::coalesce;
+%ignore TextLine::coalesce;
+%ignore NameTree;
+%ignore OutputDev::dump();
+%ignore TextOutputDev::dump();
+%ignore TextPage::dump(void *outputStream, TextOutputFunc outputFunc,
+            GBool physLayout);
+%ignore TextPool;
+
+/* we can't support overloaded constructors with the same number of args */
+%rename("with_callback") TextOutputDev::TextOutputDev(TextOutputFunc,void *,GBool,GBool);
+
+%contract Catalog::getPage(int i) {
+require:
+    i >= 1;
+}
+%contract PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
+        double hDPI, double vDPI, int rotate,
+        GBool useMediaBox, GBool crop, GBool printing,
+        GBool (*abortCheckCbk)(void *data) = NULL,
+        void *abortCheckCbkData = NULL,
+        GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data) = NULL,
+        void *annotDisplayDecideCbkData = NULL) {
+require:
+    firstPage >= 1;
+    lastPage >= 1;
+    hDPI > 0;
+    vDPI > 0;
+}
+
+%include <poppler-config.h>;
+%include <Catalog.h>;
+%include <Dict.h>;
+%include <Object.h>;
+%include <Page.h>;
+%include <XRef.h>;
+%include <PDFDoc.h>;
+%include <Annot.h>
+%include <OutputDev.h>;
+%apply double *OUTPUT { double *r, double *g, double *b, double *xMinA, double *yMinA, double *xMaxA, double *yMaxA };
+%include <TextOutputDev.h>;
+%clear double *r, double *g, double *b, double *xMinA, double *yMinA, double *xMaxA, double *yMaxA;
+
+%include "util.i"
+
+/* poppler has GlobalParams that must exist before we do anything */
+%init %{
+    globalParams = new GlobalParams();
+%}
+
-- 
1.7.2.3


--=-owvJKzHF+/8E/IQpZ/Tu--



More information about the poppler mailing list