implemented asset resolving
[swftools.git] / lib / python / gfx.c
index 5bc2068..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"
@@ -66,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)
 
 //---------------------------------------------------------------------
@@ -124,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"
@@ -165,11 +349,32 @@ 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"
@@ -184,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;
 }
@@ -202,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);
@@ -227,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*);
@@ -287,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;
     }
 }
     
@@ -327,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)
 {
@@ -345,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)
 {
@@ -391,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");
 
@@ -418,6 +657,9 @@ 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}
@@ -543,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;
@@ -677,7 +919,7 @@ 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;
 }
 
@@ -726,6 +968,8 @@ static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
                    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";
            }
@@ -734,9 +978,9 @@ static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
    
     if(!strcmp(type,"pdf"))
        self->doc = pdfdriver->open(pdfdriver,filename);
-    else if(!strcmp(type, "image")) 
+    else if(!strcmp(type, "image") || !strcmp(type, "img"))  
        self->doc = imagedriver->open(imagedriver, filename);
-    else if(!strcmp(type, "swf")) 
+    else if(!strcmp(type, "swf") || !strcmp(type, "SWF"))
        self->doc = swfdriver->open(imagedriver, filename);
     else
        return PY_ERROR("Unknown type %s", type);
@@ -875,7 +1119,7 @@ static PyObject* f_setparameter(PyObject* self, PyObject* args, PyObject* kwargs
     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;
 }
 
@@ -915,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;
 }
 
@@ -931,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;
 }
 
@@ -946,9 +1190,13 @@ static PyMethodDef pdf2swf_methods[] =
 
     /* 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}