implemented asset resolving
[swftools.git] / lib / python / gfx.c
index 239822d..0af8b65 100644 (file)
 #include <Python.h>
 #include <stdarg.h>
 #undef HAVE_STAT
+#include "../../config.h"
+#include "../gfxtools.h"
 #include "../devices/swf.h"
 #include "../devices/render.h"
+#include "../devices/ocr.h"
 #include "../devices/rescale.h"
 #include "../devices/text.h"
+#ifdef USE_OPENGL
+#include "../devices/opengl.h"
+#endif
 #include "../pdf/pdf.h"
+#include "../readers/swf.h"
+#include "../readers/image.h"
 #include "../log.h"
 #include "../utf8.h"
 
-gfxsource_t*pdfdriver;
+static gfxsource_t*pdfdriver = 0;
+static gfxsource_t*swfdriver = 0;
+static gfxsource_t*imagedriver = 0;
 
 staticforward PyTypeObject OutputClass;
 staticforward PyTypeObject PageClass;
@@ -62,11 +72,11 @@ static char* strf(char*format, ...)
     int l;
     va_list arglist;
     va_start(arglist, format);
-    vsprintf(buf, format, arglist);
+    vsnprintf(buf, sizeof(buf)-1, format, arglist);
     va_end(arglist);
     return strdup(buf);
 }
-#define PY_ERROR(s,args...) (PyErr_SetString(PyExc_Exception, strf(s, ## args)),NULL)
+#define PY_ERROR(s,args...) (PyErr_SetString(PyExc_Exception, strf(s, ## args)),(void*)NULL)
 #define PY_NONE Py_BuildValue("s", 0)
 
 //---------------------------------------------------------------------
@@ -75,7 +85,10 @@ PyDoc_STRVAR(output_save_doc, \
 "Saves the contents of an output device to a file\n"
 "Depending on what the output device is, the contents\n"
 "of the file may be plain text, an image, an SWF file,\n"
-"etc.\n");
+"etc.\n"
+"For the ImageList device, several files (named\n"
+"filename.1.png, filename.2.png etc.) might be created)\n"
+);
 static PyObject* output_save(PyObject* _self, PyObject* args, PyObject* kwargs)
 {
     OutputObject* self = (OutputObject*)_self;
@@ -117,6 +130,184 @@ static PyObject* output_startpage(PyObject* _self, PyObject* args, PyObject* kwa
     self->output_device->startpage(self->output_device, width, height);
     return PY_NONE;
 }
+
+/* as the definition of the python image type comes from another module (not
+   included here, reproduce the necessary structure and extract the image
+   without using the type definition */
+typedef struct {
+    PyObject_HEAD
+    gfximage_t*image;
+    PyObject* strrepr;
+} ImageObject;
+static gfximage_t*toImage(PyObject*_bitmap)
+{
+    if(!_bitmap || !_bitmap->ob_type->tp_name || strcmp(_bitmap->ob_type->tp_name, "Image")) {
+        PY_ERROR("Second argument to fillbitmap must be an image");
+        return 0;
+    }
+    ImageObject*bitmap = (ImageObject*)_bitmap;
+    return bitmap->image;
+}
+
+static gfxline_t*toLine(PyObject*_line)
+{
+    int t;
+    int num = PyList_Size(_line);
+    gfxline_t first;
+    first.next = 0;
+    gfxline_t*last=&first;
+    for(t=0;t<num;t++) {
+        PyObject*p= PySequence_GetItem(_line, t);
+        if(!PyTuple_Check(p)) {
+            return PY_ERROR("each point must be a tuple");
+       }
+        PyObject*_type = PyTuple_GetItem(p, 0);
+        if(!PyString_Check(_type))
+            return PY_ERROR("point tuples must start with a string");
+        char*type = PyString_AsString(_type);
+        int s;
+        int size = PyTuple_Size(p);
+        for(s=1;s<size;s++) {
+            if(!PyFloat_Check(PyTuple_GetItem(p,s))) {
+                return PY_ERROR("coordinates must be floats");
+            }
+        }
+        gfxline_t*l = (gfxline_t*)malloc(sizeof(gfxline_t));
+        memset(l, 0, sizeof(gfxline_t));
+        last->next = l;
+        last = l;
+        if(type[0]=='m') {
+            l->type = gfx_moveTo;
+            if(size!=3)
+                return PY_ERROR("need 2 values for move");
+            l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
+            l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
+        } else if(type[0]=='l') {
+            l->type = gfx_lineTo;
+            if(size!=3)
+                return PY_ERROR("need 2 values for line");
+            l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
+            l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
+        } else if(type[0]=='s') {
+            l->type = gfx_splineTo;
+            if(size!=5)
+                return PY_ERROR("need 4 values for spline");
+            l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
+            l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
+            l->sx = PyFloat_AsDouble(PyTuple_GetItem(p, 3));
+            l->sy = PyFloat_AsDouble(PyTuple_GetItem(p, 4));
+        } else {
+            return PY_ERROR("Unknown line code '%s'", type);
+        }
+    }
+    return first.next;
+}
+
+PyDoc_STRVAR(output_fillbitmap_doc, \
+"fillbitmap()\n\n"
+"fill a polygon with a bitmap pattern\n"
+);
+static PyObject* output_fillbitmap(PyObject* _self, PyObject* args, PyObject* kwargs)
+{
+    OutputObject* self = (OutputObject*)_self;
+    PyObject*_line=0;
+    PyObject*_bitmap=0;
+    static char *kwlist[] = {"line", "bitmap", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", kwlist, &PyList_Type, &_line, &_bitmap))
+       return NULL;
+
+    gfximage_t*image = toImage(_bitmap);
+    if(!image)
+        return PY_ERROR("invalid image");
+
+    gfxline_t*line = toLine(_line);
+    if(!line) 
+        return 0;
+
+    /* TODO */
+    gfxmatrix_t m;
+    memset(&m, 0, sizeof(gfxmatrix_t));
+    m.m00 = m.m11 = 1.0;
+
+    self->output_device->fillbitmap(self->output_device, line, image, &m, 0);
+    gfxline_free(line);
+    return PY_NONE;
+}
+
+PyDoc_STRVAR(output_fill_doc, \
+"fill()\n\n"
+"fill a polygon with a color\n"
+);
+static PyObject* output_fill(PyObject* _self, PyObject* args, PyObject* kwargs)
+{
+    OutputObject* self = (OutputObject*)_self;
+    PyObject*_line=0;
+    PyObject*_bitmap=0;
+    static char *kwlist[] = {"line", "color", NULL};
+
+    PyObject* color=0;
+
+    int a=255,r=0,g=0,b=0;
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", kwlist, &PyList_Type, &_line, &color))
+       return NULL;
+
+    if(!PyArg_ParseTuple(color, "iiii:color",  &a, &r, &g, &b)) {
+        return NULL;
+    }
+
+    gfxcolor_t c;
+    c.r = r; c.g = g; c.b = b; c.a = a;
+
+    gfxline_t*line = toLine(_line);
+    if(!line) 
+        return 0;
+
+    /* TODO */
+    gfxmatrix_t m;
+    memset(&m, 0, sizeof(gfxmatrix_t));
+    m.m00 = m.m11 = 1.0;
+
+    self->output_device->fill(self->output_device, line, &c);
+    gfxline_free(line);
+    return PY_NONE;
+}
+
+PyDoc_STRVAR(output_stroke_doc, \
+"stroke()\n\n"
+"stroke a polygon with a color\n"
+);
+static PyObject* output_stroke(PyObject* _self, PyObject* args, PyObject* kwargs)
+{
+    OutputObject* self = (OutputObject*)_self;
+    PyObject*_line=0;
+    PyObject*_bitmap=0;
+    static char *kwlist[] = {"line", "width", "color", NULL};
+
+    PyObject* color=0;
+
+    int a=255,r=0,g=0,b=0;
+    float width = 1.0;
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!fO", kwlist, &PyList_Type, &_line, &width, &color))
+       return NULL;
+
+    if(!PyArg_ParseTuple(color, "iiii:color",  &a, &r, &g, &b)) {
+        return NULL;
+    }
+
+    gfxcolor_t c;
+    c.r = r; c.g = g; c.b = b; c.a = a;
+
+    gfxline_t*line = toLine(_line);
+    if(!line) 
+        return 0;
+
+    self->output_device->stroke(self->output_device, line, width, &c, 
+            /*TODO*/ gfx_capRound, gfx_joinRound, 0.0);
+    gfxline_free(line);
+    return PY_NONE;
+}
+
 PyDoc_STRVAR(output_endpage_doc, \
 "endpage()\n\n"
 "Ends a page in the output device. This function should be called\n"
@@ -130,12 +321,26 @@ static PyObject* output_endpage(PyObject* _self, PyObject* args, PyObject* kwarg
     self->output_device->endpage(self->output_device);
     return PY_NONE;
 }
+PyDoc_STRVAR(output_setparameter_doc, \
+"setparameter(key, value)\n\n"
+"Set a output-device dependent parameter"
+);
+static PyObject* output_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
+{
+    OutputObject* self = (OutputObject*)_self;
+    static char *kwlist[] = {"key", "value", NULL};
+    char*key=0,*value=0;
+    if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
+       return NULL;
+    self->output_device->setparameter(self->output_device, key, value);
+    return PY_NONE;
+}
 PyDoc_STRVAR(f_createSWF_doc, \
 "SWF()\n\n"
 "Creates a device which renders documents to SWF (Flash) files.\n"
 "Depending on the way the document parser behaves (see the poly2bitmap\n"
 "and bitmap parameters), the resulting SWF might use vector operations\n"
-"and Flash Texts to display the document, or just a single bitmap\n"
+"and Flash Texts to display the document, or just a single bitmap.\n"
 );
 static PyObject* f_createSWF(PyObject* parent, PyObject* args, PyObject* kwargs)
 {
@@ -144,18 +349,38 @@ static PyObject* f_createSWF(PyObject* parent, PyObject* args, PyObject* kwargs)
        return NULL;
     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
     
-    self->output_device = malloc(sizeof(gfxdevice_t));
+    self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
     gfxdevice_swf_init(self->output_device);
     return (PyObject*)self;
 }
 
+PyDoc_STRVAR(f_createOCR_doc, \
+"OCR()\n\n"
+"Creates a device which processes documents using OCR (optical\n"
+"character recognition).\n"
+"This is handy for e.g. extracting fulltext from PDF documents\n"
+"which have broken fonts, and where hence the \"PlainText\"\n"
+"device doesn't work.\n"
+);
+static PyObject* f_createOCR(PyObject* parent, PyObject* args, PyObject* kwargs)
+{
+    static char *kwlist[] = {NULL};
+    if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
+       return NULL;
+    OutputObject*self = PyObject_New(OutputObject, &OutputClass);
+    
+    self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
+    gfxdevice_ocr_init(self->output_device);
+    return (PyObject*)self;
+}
+
+
 PyDoc_STRVAR(f_createImageList_doc, \
 "ImageList()\n\n"
 "Creates a device which renders documents to bitmaps.\n"
 "Each page that is rendered will create new bitmap.\n"
-"As, right now, the only way to access the bitmaps is\n"
-"by using the save() function on the imagelist, you can\n"
-"currently only retrieve the first bitmap/page.\n"
+"Using save(), you can save the images to a number\n"
+"of files\n"
 );
 static PyObject* f_createImageList(PyObject* parent, PyObject* args, PyObject* kwargs)
 {
@@ -164,7 +389,7 @@ static PyObject* f_createImageList(PyObject* parent, PyObject* args, PyObject* k
        return NULL;
     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
     
-    self->output_device = malloc(sizeof(gfxdevice_t));
+    self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
     gfxdevice_render_init(self->output_device);
     return (PyObject*)self;
 }
@@ -182,17 +407,37 @@ static PyObject* f_createPlainText(PyObject* parent, PyObject* args, PyObject* k
        return NULL;
     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
     
-    self->output_device = malloc(sizeof(gfxdevice_t));
+    self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
     gfxdevice_text_init(self->output_device);
     return (PyObject*)self;
 }
 
-static PyObject*callback_python(char*function, gfxdevice_t*dev, const char*format, ...)
+#ifdef USE_OPENGL
+PyDoc_STRVAR(f_createOpenGL_doc, \
+"OpenGL()\n\n"
+"Creates a device which renders everything to OpenGL.\n"
+"Can be used for desktop display and debugging.\n"
+"This device is not available on all systems.\n"
+);
+static PyObject* f_createOpenGL(PyObject* parent, PyObject* args, PyObject* kwargs)
 {
-    OutputObject*self = (OutputObject*)dev->internal;
+    static char *kwlist[] = {NULL};
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
+       return NULL;
+    OutputObject*self = PyObject_New(OutputObject, &OutputClass);
     
+    self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
+    gfxdevice_opengl_init(self->output_device);
+    return (PyObject*)self;
+}
+#endif
+
+static char callback_python(char*function, gfxdevice_t*dev, const char*format, ...)
+{
+    OutputObject*self = (OutputObject*)dev->internal;
+
     if(!PyObject_HasAttrString(self->pyobj, function))
-        return PY_NONE;
+        return 0;
 
     va_list ap;
     va_start(ap, format);
@@ -207,14 +452,17 @@ static PyObject*callback_python(char*function, gfxdevice_t*dev, const char*forma
         } else if(p=='i') {
             int i = va_arg(ap, int);
             PyTuple_SetItem(tuple, pos, PyInt_FromLong(i));
+        } else if(p=='d') {
+            int i = va_arg(ap, double);
+            PyTuple_SetItem(tuple, pos, PyFloat_FromDouble(i));
         } else if(p=='c') {
             void* ptr = va_arg(ap, void*);
             gfxcolor_t*col = (gfxcolor_t*)ptr;
             PyObject*colobj = PyTuple_New(4);
-            PyTuple_SetItem(colobj, 0, PyInt_FromLong(col->r));
-            PyTuple_SetItem(colobj, 1, PyInt_FromLong(col->g));
-            PyTuple_SetItem(colobj, 2, PyInt_FromLong(col->b));
-            PyTuple_SetItem(colobj, 3, PyInt_FromLong(col->a));
+            PyTuple_SetItem(colobj, 0, PyInt_FromLong(col->a));
+            PyTuple_SetItem(colobj, 1, PyInt_FromLong(col->r));
+            PyTuple_SetItem(colobj, 2, PyInt_FromLong(col->g));
+            PyTuple_SetItem(colobj, 3, PyInt_FromLong(col->b));
             PyTuple_SetItem(tuple, pos, colobj);
         } else if(p=='l') {
             void* ptr = va_arg(ap, void*);
@@ -267,10 +515,10 @@ static PyObject*callback_python(char*function, gfxdevice_t*dev, const char*forma
     if(!result) { 
         PyErr_Print();
         PyErr_Clear();
-        return 0;
+        return 1;
     } else {
         Py_DECREF(result);
-        return 0;
+        return 1;
     }
 }
     
@@ -307,7 +555,7 @@ static void my_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolo
         joint = "round";
     else if(joint_style == gfx_joinBevel)
         joint = "bevel";
-    callback_python("stroke", dev, "licssi", line, width, color, cap, joint, miterLimit);
+    callback_python("stroke", dev, "ldcssi", line, width, color, cap, joint, miterLimit);
 }
 static void my_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
 {
@@ -325,9 +573,19 @@ static void my_addfont(gfxdevice_t*dev, gfxfont_t*font)
 {
     callback_python("addfont", dev, "f", font);
 }
-static void my_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
+static void my_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
 {
-    callback_python("drawchar", dev, "ficm", font, glyph, color, matrix);
+    if(!callback_python("drawchar", dev, "ficm", font, glyphnr, color, matrix))
+    {
+        if(!font)
+            return;
+        gfxglyph_t*glyph = &font->glyphs[glyphnr];
+        gfxline_t*line2 = gfxline_clone(glyph->line);
+        gfxline_transform(line2, matrix);
+        my_fill(dev, line2, color);
+        gfxline_free(line2);
+        return;
+    }
 }
 static void my_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
 {
@@ -350,7 +608,6 @@ PyDoc_STRVAR(f_createPassThrough_doc, \
 "to page.render().\n"
 "device needs to be a class implementing at least the following functions:\n\n"
 "setparameter(key,value)\n"
-"startpage(width,height)\n"
 "startclip(outline)\n"
 "endclip()\n"
 "stroke(outline, width, color, capstyle, jointstyle, miterLimit)\n"
@@ -360,9 +617,8 @@ PyDoc_STRVAR(f_createPassThrough_doc, \
 "addfont(font)\n"
 "drawchar(font, glyph, color, matrix)\n"
 "drawlink(outline, url)\n"
-"finish()\n\n"
 "If any of these functions are not defined, a error message will be printed,\n"
-"however the rendering process will be continued.\n"
+"however the rendering process will *not* be aborted.\n"
 );
 static PyObject* f_createPassThrough(PyObject* parent, PyObject* args, PyObject* kwargs)
 {
@@ -373,7 +629,8 @@ static PyObject* f_createPassThrough(PyObject* parent, PyObject* args, PyObject*
     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
    
     self->pyobj = obj;
-    self->output_device = malloc(sizeof(gfxdevice_t));
+    Py_INCREF(obj);
+    self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
     memset(self->output_device, 0, sizeof(gfxdevice_t));
     self->output_device->name = strdup("passthrough");
 
@@ -400,7 +657,11 @@ static PyMethodDef output_methods[] =
     /* Output functions */
     {"save", (PyCFunction)output_save, METH_KEYWORDS, output_save_doc},
     {"startpage", (PyCFunction)output_startpage, METH_KEYWORDS, output_startpage_doc},
+    {"fill", (PyCFunction)output_fill, METH_KEYWORDS, output_fill_doc},
+    {"fillbitmap", (PyCFunction)output_fillbitmap, METH_KEYWORDS, output_fillbitmap_doc},
+    {"stroke", (PyCFunction)output_stroke, METH_KEYWORDS, output_stroke_doc},
     {"endpage", (PyCFunction)output_endpage, METH_KEYWORDS, output_endpage_doc},
+    {"setparameter", (PyCFunction)output_setparameter, METH_KEYWORDS, output_setparameter_doc},
     {0,0,0,0}
 };
 
@@ -497,7 +758,7 @@ static PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs)
 PyDoc_STRVAR(page_asImage_doc, \
 "asImage(width, height)\n\n"
 "Creates a bitmap from a page. The bitmap will be returned as a string\n"
-"containing RGB triplets. The bitmap will have the specified width and\n"
+"containing RGB triplets. The bitmap will be rescaled to the specified width and\n"
 "height. The aspect ratio of width and height doesn't need to be the same\n"
 "as the page.\n"
 );
@@ -524,7 +785,7 @@ static PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs)
     gfxresult_t*result = dev2.finish(&dev2);
     gfximage_t*img = (gfximage_t*)result->get(result,"page0");
     int l = img->width*img->height;
-    unsigned char*data = malloc(img->width*img->height*3);
+    unsigned char*data = (unsigned char*)malloc(img->width*img->height*3);
     int s,t;
     for(t=0,s=0;t<l;s+=3,t++) {
        data[s+0] = img->data[t].r;
@@ -589,14 +850,13 @@ static int page_print(PyObject * _self, FILE *fi, int flags)
 
 PyDoc_STRVAR(doc_getPage_doc,
 "getPage(nr)\n\n"
-"\n"
 "Get one page from a document file. The nr parameter specifies\n"
 "which page to retrieve. Counting starts at 1, so the first page\n"
 "can be retrieved by\n"
 "    page = doc.getPage(1)\n"
 ".\n"
 "You can find out how many pages a document contains by querying\n"
-"it's pages field (doc.pages)\n"
+"its pages field (doc.pages)\n"
 );
 static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
 {
@@ -621,7 +881,6 @@ static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
 
 PyDoc_STRVAR(doc_getInfo_doc,
 "getInfo(key)\n\n"
-"\n"
 "Retrieve some information about a document. For PDF files, key\n"
 "can have the following values:\n\n"
 "\"title\", \"subject\", \"keywords\", \"author\", \"creator\", \"producer\",\n"
@@ -644,15 +903,14 @@ static PyObject* doc_getInfo(PyObject* _self, PyObject* args, PyObject* kwargs)
     return PyString_FromString(s);
 }
 
-PyDoc_STRVAR(doc_setParameter_doc,
-"setParameter(key, value)\n\n"
-"\n"
+PyDoc_STRVAR(doc_setparameter_doc,
+"setparameter(key, value)\n\n"
 "Pass a parameter or setting to the document parser. Unlike\n"
-"the module level setoption() function, the parameters set\n"
-"using setParameter will only be valid for the object itself\n"
+"the module level setparameter() function, the parameters set\n"
+"using setparameter will only be valid for the object itself\n"
 "during its lifetime.\n"
 );
-static PyObject* doc_setParameter(PyObject* _self, PyObject* args, PyObject* kwargs)
+static PyObject* doc_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
 {
     DocObject* self = (DocObject*)_self;
 
@@ -661,39 +919,69 @@ static PyObject* doc_setParameter(PyObject* _self, PyObject* args, PyObject* kwa
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key,&value))
        return NULL;
 
-    self->doc->set_parameter(self->doc, key, value);
+    self->doc->setparameter(self->doc, key, value);
     return PY_NONE;
 }
 
 PyDoc_STRVAR(f_open_doc,
 "open(type, filename) -> object\n\n"
-"Open a PDF file. The type argument always has to be \"pdf\"\n"
-"It returns a doc object which can be used to process the pdf\n"
-"contents. E.g.\n"
+"Open a PDF, SWF or image file. The type argument should be \"pdf\",\n"
+"\"swf\" or \"image\" accordingly. It returns a doc object which can be\n"
+"used to process the file contents.\n"
+"E.g.\n"
 "    doc = open(\"pdf\", \"document.pdf\")\n"
-"If the file is not a PDF file or is encrypted without\n"
+"    doc = open(\"swf\", \"flashfile.swf\")\n"
+"    doc = open(\"image\", \"image.png\")\n"
+"If the file could not be loaded, or is a encrypted PDF file without\n"
 "a proper password specified, an exception is being raised.\n"
 "If the filename argument contains a '|' char, everything behind\n"
 "the '|' is treated as password used for opening the file.\n"
 "E.g.\n"
 "    doc = open(\"pdf\", \"document.pdf|mysecretpassword\")\n"
+".\n"
+"Notice that for image files, the only supported file formats right now\n"
+"are jpeg and png.\n"
 );
 static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
 {
     static char *kwlist[] = {"type", "filename", NULL};
-    char*filename;
-    char*type;
+    char*filename=0;
+    char*type=0;
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
-        type = "pdf";
+       static char *kwlist2[] = {"filename", NULL};
+        type = 0;
        PyErr_Clear();
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist2, &filename))
            return NULL;
     }
 
     DocObject*self = PyObject_New(DocObject, &DocClass);
+
+    if(!type) { //autodetect
+       type = "pdf"; //default
+       int l = strlen(filename);
+       if(l>4) {
+           if(filename[l-4]=='.') {
+               if(strchr("pP", filename[l-3]) && strchr("dD", filename[l-2]) && strchr("fF", filename[l-1]))
+                   type = "pdf";
+               if(strchr("jJ", filename[l-3]) && strchr("pP", filename[l-2]) && strchr("gG", filename[l-1]))
+                   type = "image";
+               if(strchr("pP", filename[l-3]) && strchr("nN", filename[l-2]) && strchr("gG", filename[l-1]))
+                   type = "image";
+               if(strchr("sS", filename[l-3]) && strchr("wW", filename[l-2]) && strchr("fF", filename[l-1]))
+                   type = "swf";
+           } else if(filename[l-5]=='.') {
+               type = "image";
+           }
+       }
+    }
    
     if(!strcmp(type,"pdf"))
        self->doc = pdfdriver->open(pdfdriver,filename);
+    else if(!strcmp(type, "image") || !strcmp(type, "img"))  
+       self->doc = imagedriver->open(imagedriver, filename);
+    else if(!strcmp(type, "swf") || !strcmp(type, "SWF"))
+       self->doc = swfdriver->open(imagedriver, filename);
     else
        return PY_ERROR("Unknown type %s", type);
 
@@ -710,7 +998,7 @@ static PyMethodDef doc_methods[] =
     /* PDF functions */
     {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, doc_getPage_doc},
     {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, doc_getInfo_doc},
-    {"setParameter", (PyCFunction)doc_setParameter, METH_KEYWORDS, doc_setParameter_doc},
+    {"setparameter", (PyCFunction)doc_setparameter, METH_KEYWORDS, doc_setparameter_doc},
     {0,0,0,0}
 };
 
@@ -754,7 +1042,7 @@ PyDoc_STRVAR(output_doc,
 "object directly (i.e., from a class), however you can\n"
 "use a PassThrough() device to pass things over to Python.\n"
 "Examples for classes implementing the Output class are: \n"
-"ImageList, SWF, PlainText, PassThrough\n"
+"ImageList, SWF, PlainText and PassThrough.\n"
 );
 static PyTypeObject OutputClass =
 {
@@ -794,7 +1082,8 @@ PyDoc_STRVAR(doc_doc,
 "A Doc object is used for storing a document (like a PDF).\n"
 "doc.pages contains the number of pages in the document,\n"
 "and doc.filename the name of the file the document was\n"
-"created (loaded) from\n"
+"created (loaded) from. If the document was created from\n"
+"an image file, the number of pages is always 1\n"
 );
 static PyTypeObject DocClass =
 {
@@ -813,25 +1102,24 @@ static PyTypeObject DocClass =
 
 //=====================================================================
 
-PyDoc_STRVAR(f_setoption_doc, \
-"setoption(key,value)\n\n"
-"\n"
+PyDoc_STRVAR(f_setparameter_doc, \
+"setparameter(key,value)\n\n"
 "Set a parameter in the gfx module (which might affect the PDF\n"
 "parser or any of the rendering backends). This is a parameter\n"
-"which would usually be passed with the \"-s\" option to pdf2swf\n"
+"which would usually be passed with the \"-s\" option to pdf2swf.\n"
 "For a list of all parameters, see the output of\n"
 "    pdf2swf -s help\n"
 "and\n"
 "    pdf2swf somefile.pdf -s help\n"
 ".\n"
 );
-static PyObject* f_setoption(PyObject* self, PyObject* args, PyObject* kwargs)
+static PyObject* f_setparameter(PyObject* self, PyObject* args, PyObject* kwargs)
 {
     static char *kwlist[] = {"key", "value", NULL};
     char*key=0,*value=0;
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
        return NULL;
-    pdfdriver->set_parameter(pdfdriver,key,value);
+    pdfdriver->setparameter(pdfdriver,key,value);
     return PY_NONE;
 }
 
@@ -871,7 +1159,7 @@ static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
     char*filename=0;
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
        return NULL;
-    pdfdriver->set_parameter(pdfdriver,"font", filename);
+    pdfdriver->setparameter(pdfdriver,"font", filename);
     return PY_NONE;
 }
 
@@ -887,7 +1175,7 @@ static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
     char*filename=0;
     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
        return NULL;
-    pdfdriver->set_parameter(pdfdriver,"fontdir", filename);
+    pdfdriver->setparameter(pdfdriver,"fontdir", filename);
     return PY_NONE;
 }
 
@@ -897,14 +1185,18 @@ static PyMethodDef pdf2swf_methods[] =
     {"open", (PyCFunction)f_open, METH_KEYWORDS, f_open_doc},
     {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, f_addfont_doc},
     {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, f_addfontdir_doc},
-    {"setoption", (PyCFunction)f_setoption, METH_KEYWORDS, f_setoption_doc},
+    {"setparameter", (PyCFunction)f_setparameter, METH_KEYWORDS, f_setparameter_doc},
     {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, f_verbose_doc},
 
     /* devices */
     {"SWF", (PyCFunction)f_createSWF, METH_KEYWORDS, f_createSWF_doc},
+    {"OCR", (PyCFunction)f_createOCR, METH_KEYWORDS, f_createOCR_doc},
     {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, f_createImageList_doc},
     {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, f_createPlainText_doc},
     {"PassThrough", (PyCFunction)f_createPassThrough, METH_KEYWORDS, f_createPassThrough_doc},
+#ifdef USE_OPENGL
+    {"OpenGL", (PyCFunction)f_createOpenGL, METH_KEYWORDS, f_createOpenGL_doc},
+#endif
 
     /* sentinel */
     {0, 0, 0, 0}
@@ -917,7 +1209,7 @@ PyDoc_STRVAR(gfx_doc, \
 "The latter functionality is similar to what is offered by swftools'\n" 
 "(http://www.swftools.org) pdf2swf utility, however more powerful-\n" 
 "You can also create individual SWF files from single pages of the PDF\n" 
-"or combine more than one page into a bigger PDF.\n"
+"or mix pages from different PDF files.\n"
 );
 
 void initgfx(void)
@@ -928,6 +1220,8 @@ void initgfx(void)
     DocClass.ob_type = &PyType_Type;
 
     pdfdriver = gfxsource_pdf_create();
+    swfdriver = gfxsource_swf_create();
+    imagedriver = gfxsource_image_create();
     
     PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
     PyObject*module_dict = PyModule_GetDict(module);