Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#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;
-staticforward PyTypeObject DriverClass;
+staticforward PyTypeObject DocClass;
typedef struct {
PyObject_HEAD
gfxdevice_t*output_device;
+ PyObject*pyobj; //only for passthrough
} OutputObject;
typedef struct {
#define PY_NONE Py_BuildValue("s", 0)
//---------------------------------------------------------------------
-staticforward PyObject* output_save(PyObject* _self, PyObject* args, PyObject* kwargs);
-staticforward PyObject* output_startpage(PyObject* _self, PyObject* args, PyObject* kwargs);
-staticforward PyObject* output_endpage(PyObject* _self, PyObject* args, PyObject* kwargs);
-
-static PyMethodDef output_methods[] =
-{
- /* Output functions */
- {"save", (PyCFunction)output_save, METH_KEYWORDS, ""},
- {"startpage", (PyCFunction)output_startpage, METH_KEYWORDS, ""},
- {"endpage", (PyCFunction)output_endpage, METH_KEYWORDS, ""},
- {0,0,0,0}
-};
+PyDoc_STRVAR(output_save_doc, \
+"save(filename)\n\n"
+"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"
+"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;
result->destroy(result);
return PY_NONE;
}
+
+PyDoc_STRVAR(output_startpage_doc, \
+"startpage(width, height)\n\n"
+"Starts a new page/frame in the output device.\n"
+"The usual way to render documents is to start a new page in the\n"
+"device for each page in the document:\n"
+"\n"
+"for pagenr in range(1,doc.pages+1):\n"
+" page = doc.getPage(pagenr)\n"
+" output.startpage(page.width, page.height)\n"
+" page.render(output)\n"
+" output.endpage()\n"
+"\n"
+"It is, however, also possible to render more than one document page\n"
+"to a single output page. E.g. for side-by-side or book views.\n"
+);
static PyObject* output_startpage(PyObject* _self, PyObject* args, PyObject* kwargs)
{
OutputObject* self = (OutputObject*)_self;
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")) {
+ return PY_ERROR("Second argument to fillbitmap must be an image");
+ }
+ 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 = 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"
+"once for every startpage()\n"
+);
static PyObject* output_endpage(PyObject* _self, PyObject* args, PyObject* kwargs)
{
OutputObject* self = (OutputObject*)_self;
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"
+);
static PyObject* f_createSWF(PyObject* parent, PyObject* args, PyObject* kwargs)
{
static char *kwlist[] = {NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
+ if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
return NULL;
OutputObject*self = PyObject_New(OutputObject, &OutputClass);
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 = 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"
+"Using save(), you can save the images to a number\n"
+"of files\n"
+);
static PyObject* f_createImageList(PyObject* parent, PyObject* args, PyObject* kwargs)
{
static char *kwlist[] = {NULL};
gfxdevice_render_init(self->output_device);
return (PyObject*)self;
}
+
+PyDoc_STRVAR(f_createPlainText_doc, \
+"PlainText()\n\n"
+"Creates a device which can be used to extract text from documents,\n"
+"by passing it as parameter to page.render().\n"
+"The extracted text can be saved by plaintext.save(filename).\n"
+);
static PyObject* f_createPlainText(PyObject* parent, PyObject* args, PyObject* kwargs)
{
static char *kwlist[] = {NULL};
return (PyObject*)self;
}
+#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)
+{
+ static char *kwlist[] = {NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
+ return NULL;
+ OutputObject*self = PyObject_New(OutputObject, &OutputClass);
+
+ self->output_device = malloc(sizeof(gfxdevice_t));
+ gfxdevice_opengl_init(self->output_device);
+ return (PyObject*)self;
+}
+#endif
+
+static PyObject*callback_python(char*function, gfxdevice_t*dev, const char*format, ...)
+{
+ OutputObject*self = (OutputObject*)dev->internal;
+
+ if(!PyObject_HasAttrString(self->pyobj, function))
+ return PY_NONE;
+
+ va_list ap;
+ va_start(ap, format);
+
+ PyObject*tuple = PyTuple_New(strlen(format));
+ int pos = 0;
+ while(format[pos]) {
+ char p = format[pos];
+ if(p=='s') {
+ char*s = va_arg(ap, char*);
+ PyTuple_SetItem(tuple, pos, PyString_FromString(s));
+ } else if(p=='i') {
+ int i = va_arg(ap, int);
+ PyTuple_SetItem(tuple, pos, PyInt_FromLong(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->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*);
+ gfxline_t*line = (gfxline_t*)ptr;
+ gfxline_t*l;
+ int len = 0, i = 0;
+ l = line;
+ while(l) {l=l->next;len++;}
+ PyObject*list = PyList_New(len);
+ l = line;
+ while(l) {
+ PyObject*point=0;
+ if(l->type == gfx_moveTo) {
+ point = PyTuple_New(3);
+ PyTuple_SetItem(point, 0, PyString_FromString("m"));
+ PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
+ PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
+ } else if(l->type == gfx_lineTo) {
+ point = PyTuple_New(3);
+ PyTuple_SetItem(point, 0, PyString_FromString("l"));
+ PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
+ PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
+ } else if(l->type == gfx_splineTo) {
+ point = PyTuple_New(5);
+ PyTuple_SetItem(point, 0, PyString_FromString("s"));
+ PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
+ PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
+ PyTuple_SetItem(point, 3, PyFloat_FromDouble(l->sx));
+ PyTuple_SetItem(point, 4, PyFloat_FromDouble(l->sy));
+ } else {
+ point = PY_NONE;
+ }
+ PyList_SetItem(list, i, point);
+ l = l->next;
+ i++;
+ }
+ PyTuple_SetItem(tuple, pos, list);
+ } else {
+ PyTuple_SetItem(tuple, pos, PY_NONE);
+ }
+ pos++;
+ }
+ va_end(ap);
+ PyObject*f = PyObject_GetAttrString(self->pyobj, function);
+ if(!f)
+ return 0;
+ PyErr_Clear();
+ PyObject* result = PyObject_CallObject(f, tuple);
+
+ if(!result) {
+ PyErr_Print();
+ PyErr_Clear();
+ return 0;
+ } else {
+ Py_DECREF(result);
+ return 0;
+ }
+}
+
+static int my_setparameter(gfxdevice_t*dev, const char*key, const char*value)
+{
+ callback_python("setparameter", dev, "ss", key, value);
+ return 1;
+}
+static void my_startpage(gfxdevice_t*dev, int width, int height)
+{
+ callback_python("startpage", dev, "ii", width, height);
+}
+static void my_startclip(gfxdevice_t*dev, gfxline_t*line)
+{
+ callback_python("startclip", dev, "l", line);
+}
+static void my_endclip(gfxdevice_t*dev)
+{
+ callback_python("endclip", dev, "");
+}
+static void my_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
+{
+ char*cap = 0;
+ char*joint = 0;
+ if(cap_style == gfx_capButt)
+ cap = "butt";
+ else if(cap_style == gfx_capRound)
+ cap = "round";
+ else if(cap_style == gfx_capSquare)
+ cap = "square";
+ if(joint_style == gfx_joinMiter)
+ joint = "miter";
+ else if(joint_style == gfx_joinRound)
+ joint = "round";
+ else if(joint_style == gfx_joinBevel)
+ joint = "bevel";
+ callback_python("stroke", dev, "licssi", line, width, color, cap, joint, miterLimit);
+}
+static void my_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
+{
+ callback_python("fill", dev, "lc", line, color);
+}
+static void my_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*imgcoord2devcoord, gfxcxform_t*cxform)
+{
+ callback_python("fillbitmap", dev, "lImx", line, img, imgcoord2devcoord, cxform);
+}
+static void my_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
+{
+ callback_python("fillgradient", dev, "lgsm", line, gradient, type, matrix);
+}
+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)
+{
+ callback_python("drawchar", dev, "ficm", font, glyph, color, matrix);
+}
+static void my_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
+{
+ callback_python("drawlink", dev, "ls", line, action);
+}
+static void my_endpage(gfxdevice_t*dev)
+{
+ callback_python("drawlink", dev, "");
+}
+static gfxresult_t* my_finish(gfxdevice_t*dev)
+{
+ callback_python("finish", dev, "");
+ return 0;
+}
+
+
+PyDoc_STRVAR(f_createPassThrough_doc, \
+"PassThrough(device)\n\n"
+"Creates a PassThrough device, which can be used as parameter in calls\n"
+"to page.render().\n"
+"device needs to be a class implementing at least the following functions:\n\n"
+"setparameter(key,value)\n"
+"startclip(outline)\n"
+"endclip()\n"
+"stroke(outline, width, color, capstyle, jointstyle, miterLimit)\n"
+"fill(outline, color)\n"
+"fillbitmap(outline, image, matrix, colortransform)\n"
+"fillgradient(outline, gradient, gradienttype, matrix)\n"
+"addfont(font)\n"
+"drawchar(font, glyph, color, matrix)\n"
+"drawlink(outline, url)\n"
+"If any of these functions are not defined, a error message will be printed,\n"
+"however the rendering process will *not* be aborted.\n"
+);
+static PyObject* f_createPassThrough(PyObject* parent, PyObject* args, PyObject* kwargs)
+{
+ static char *kwlist[] = {"device", NULL};
+ PyObject*obj;
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &obj))
+ return NULL;
+ OutputObject*self = PyObject_New(OutputObject, &OutputClass);
+
+ self->pyobj = obj;
+ Py_INCREF(obj);
+ self->output_device = malloc(sizeof(gfxdevice_t));
+ memset(self->output_device, 0, sizeof(gfxdevice_t));
+ self->output_device->name = strdup("passthrough");
+
+ self->output_device->setparameter = my_setparameter;
+ self->output_device->startpage = my_startpage;
+ self->output_device->startclip = my_startclip;
+ self->output_device->addfont = my_addfont;
+ self->output_device->endclip = my_endclip;
+ self->output_device->stroke = my_stroke;
+ self->output_device->fill = my_fill;
+ self->output_device->fillbitmap = my_fillbitmap;
+ self->output_device->fillgradient = my_fillgradient;
+ self->output_device->drawchar = my_drawchar;
+ self->output_device->drawlink = my_drawlink;
+ self->output_device->endpage = my_endpage;
+ self->output_device->finish = my_finish;
+ self->output_device->internal = self;
+
+ return (PyObject*)self;
+}
+
+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}
+};
static void output_dealloc(PyObject* _self) {
OutputObject* self = (OutputObject*)_self;
if(self->output_device) {
gfxresult_t*result = self->output_device->finish(self->output_device);
- result->destroy(result);result=0;
+ if(result) {
+ result->destroy(result);result=0;
+ }
self->output_device = 0;
}
static PyObject* output_getattr(PyObject * _self, char* a)
{
OutputObject*self = (OutputObject*)_self;
-
+
/* if(!strcmp(a, "x1")) {
return PyInt_FromLong(self->output_device->x1);
} else if(!strcmp(a, "y1")) {
staticforward PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs);
staticforward PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs);
-static PyMethodDef page_methods[] =
-{
- /* Page functions */
- {"render", (PyCFunction)page_render, METH_KEYWORDS, ""},
- {"asImage", (PyCFunction)page_asImage, METH_KEYWORDS, ""},
- {0,0,0,0}
-};
+PyDoc_STRVAR(page_render_doc, \
+"render(output, move=(0,0), clip=None)\n\n"
+"Renders a page to the rendering backend specified by the output\n"
+"parameter. Rendering consists of calling a number of functions on the\n"
+"output device, see the description of the \"PassThrough\" device.\n"
+"The page may be shifted to a given position using the move parameter,\n"
+"and may also be clipped to a specific size using the clip parameter.\n"
+"The clipping operation is applied after the move operation.\n"
+);
static PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs)
{
PageObject* self = (PageObject*)_self;
return PY_NONE;
}
+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 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"
+);
static PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs)
{
PageObject* self = (PageObject*)_self;
gfxdevice_t dev1,dev2;
gfxdevice_render_init(&dev1);
dev1.setparameter(&dev1, "antialise", "2");
- gfxdevice_rescale_init(&dev2, &dev1, width, height);
+ gfxdevice_rescale_init(&dev2, &dev1, width, height, 0);
dev2.startpage(&dev2, self->page->width, self->page->height);
self->page->render(self->page, &dev2);
dev2.endpage(&dev2);
return PyString_FromStringAndSize((char*)data,img->width*img->height*3);
}
+static PyMethodDef page_methods[] =
+{
+ /* Page functions */
+ {"render", (PyCFunction)page_render, METH_KEYWORDS, page_render_doc},
+ {"asImage", (PyCFunction)page_asImage, METH_KEYWORDS, page_asImage_doc},
+ {0,0,0,0}
+};
static void page_dealloc(PyObject* _self) {
PageObject* self = (PageObject*)_self;
if(self->page) {
PyObject_Del(self);
}
+
static PyObject* page_getattr(PyObject * _self, char* a)
{
PageObject*self = (PageObject*)_self;
}
return Py_FindMethod(page_methods, _self, a);
}
+
static int page_setattr(PyObject * self, char* a, PyObject * o) {
return -1;
}
//---------------------------------------------------------------------
-staticforward PyObject* doc_getPage(PyObject* parent, PyObject* args, PyObject* kwargs);
-staticforward PyObject* doc_getInfo(PyObject* parent, PyObject* args, PyObject* kwargs);
-
-static PyMethodDef doc_methods[] =
-{
- /* PDF functions */
- {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, ""},
- {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, ""},
- {0,0,0,0}
-};
-
+PyDoc_STRVAR(doc_getPage_doc,
+"getPage(nr)\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"
+"its pages field (doc.pages)\n"
+);
static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
{
DocObject* self = (DocObject*)_self;
return (PyObject*)page;
}
+PyDoc_STRVAR(doc_getInfo_doc,
+"getInfo(key)\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"
+"\"creationdate\", \"moddate\", \"linearized\", \"tagged\", \"encrypted\",\n"
+"\"oktoprint\", \"oktocopy\", \"oktochange\", \"oktoaddnotes\", \"version\".\n\n"
+"If the \"oktocopy\" digital rights management flag is set to \"no\", then the\n"
+"pdf parser won't allow you to access the PDF file. Trying to extract pages\n"
+"from it will raise an exception.\n"
+);
static PyObject* doc_getInfo(PyObject* _self, PyObject* args, PyObject* kwargs)
{
DocObject* self = (DocObject*)_self;
return PyString_FromString(s);
}
+PyDoc_STRVAR(doc_setparameter_doc,
+"setparameter(key, value)\n\n"
+"Pass a parameter or setting to the document parser. Unlike\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)
+{
+ DocObject* self = (DocObject*)_self;
+
+ static char *kwlist[] = {"key", "value", NULL};
+ char*key = 0, *value=0;
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key,&value))
+ return NULL;
+
+ self->doc->set_parameter(self->doc, key, value);
+ return PY_NONE;
+}
+
+PyDoc_STRVAR(f_open_doc,
+"open(type, filename) -> object\n\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"
+" 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;
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename))
- return NULL;
+ char*filename=0;
+ char*type=0;
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
+ static char *kwlist2[] = {"filename", NULL};
+ type = 0;
+ PyErr_Clear();
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist2, &filename))
+ return NULL;
+ }
- DocObject*self = PyObject_New(DocObject, &DriverClass);
+ 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(filename);
+ 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);
self->filename = strdup(filename);
return (PyObject*)self;
}
+
+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},
+ {0,0,0,0}
+};
+
static void doc_dealloc(PyObject* _self) {
DocObject* self = (DocObject*)_self;
if(self->doc) {
//---------------------------------------------------------------------
+PyDoc_STRVAR(output_doc,
+"An Output object can be used as parameter to the render()\n"
+"call of a page. It's not possible to create this type of\n"
+"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 and PassThrough.\n"
+);
static PyTypeObject OutputClass =
{
PyObject_HEAD_INIT(NULL)
0,
- tp_name: "Output",
+ tp_name: "gfx.Output",
tp_basicsize: sizeof(OutputObject),
tp_itemsize: 0,
tp_dealloc: output_dealloc,
tp_print: output_print,
tp_getattr: output_getattr,
tp_setattr: output_setattr,
+ tp_doc: output_doc,
+ tp_methods: output_methods
};
+PyDoc_STRVAR(page_doc,
+"A Page object contains a single page of a document.\n"
+"page.width and page.height (or page.size) contain the\n"
+"page dimensions. page.nr is the number of the page, and\n"
+"page.doc is the parent document.\n"
+);
static PyTypeObject PageClass =
{
PyObject_HEAD_INIT(NULL)
0,
- tp_name: "Page",
+ tp_name: "gfx.Page",
tp_basicsize: sizeof(PageObject),
tp_itemsize: 0,
tp_dealloc: page_dealloc,
tp_print: page_print,
tp_getattr: page_getattr,
tp_setattr: page_setattr,
+ tp_doc: page_doc,
+ tp_methods: page_methods
};
-static PyTypeObject DriverClass =
+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. If the document was created from\n"
+"an image file, the number of pages is always 1\n"
+);
+static PyTypeObject DocClass =
{
PyObject_HEAD_INIT(NULL)
0,
- tp_name: "PDF",
+ tp_name: "gfx.Doc",
tp_basicsize: sizeof(DocObject),
tp_itemsize: 0,
tp_dealloc: doc_dealloc,
tp_print: doc_print,
tp_getattr: doc_getattr,
tp_setattr: doc_setattr,
+ tp_doc: doc_doc,
+ tp_methods: doc_methods,
};
//=====================================================================
-static PyObject* f_setoption(PyObject* self, PyObject* args, PyObject* kwargs)
+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"
+"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_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(key,value);
+ pdfdriver->set_parameter(pdfdriver,key,value);
return PY_NONE;
}
+PyDoc_STRVAR(f_verbose_doc, \
+"verbose(level)\n\n"
+"Set the logging verbosity of the gfx module. Log levels are:\n"
+"level=-1 Log nothing\n"
+"level=0 (fatal) Log only fatal errors\n"
+"level=1 (error) Log only fatal errors and errors\n"
+"level=2 (warn) Log all errors and warnings\n"
+"level=3 (notice) Log also some rudimentary data about the parsing/conversion\n"
+"level=4 (verbose) Log some additional parsing information\n"
+"level=5 (debug) Log debug statements\n"
+"level=6 (trace) Log extended debug statements\n"
+"All logging messages are written to stdout.\n"
+);
static PyObject* f_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
{
static char *kwlist[] = {"val", NULL};
return PY_NONE;
}
+PyDoc_STRVAR(f_addfont_doc, \
+"addfont(filename)\n\n"
+"Passes an additional font file to the PDF parser. If a PDF contains\n"
+"external fonts (i.e. fonts which are not contained in the PDF itself)\n"
+"then the files added by addfont() will be searched.\n"
+);
+
static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
{
static char *kwlist[] = {"filename", NULL};
char*filename=0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
return NULL;
- pdfdriver->set_parameter("font", filename);
+ pdfdriver->set_parameter(pdfdriver,"font", filename);
return PY_NONE;
}
+PyDoc_STRVAR(f_addfontdir_doc, \
+"addfontdir(dirname)\n\n"
+"Passes a complete directory containing fonts to the PDF parser. Any\n"
+"font file within this directory might be used to resolve external fonts\n"
+"in PDF files\n"
+);
static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
{
static char *kwlist[] = {"filename", NULL};
char*filename=0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
return NULL;
- pdfdriver->set_parameter("fontdir", filename);
+ pdfdriver->set_parameter(pdfdriver,"fontdir", filename);
return PY_NONE;
}
static PyMethodDef pdf2swf_methods[] =
{
/* sources */
- {"open", (PyCFunction)f_open, METH_KEYWORDS, ""},
- {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, ""},
- {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, ""},
- {"setoption", (PyCFunction)f_setoption, METH_KEYWORDS, ""},
- {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, ""},
+ {"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},
+ {"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, ""},
- {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, ""},
- {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, ""},
+ {"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}
};
+PyDoc_STRVAR(gfx_doc, \
+"This module contains a PDF parser (based on xpdf) and a number of\n"
+"rendering backends. In particular, it can extract text from PDF pages,\n"
+"create bitmaps from them, or convert PDF files to SWF.\n"
+"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 mix pages from different PDF files.\n"
+);
+
void initgfx(void)
{
initLog(0,0,0,0,0,2);
OutputClass.ob_type = &PyType_Type;
PageClass.ob_type = &PyType_Type;
- DriverClass.ob_type = &PyType_Type;
-
+ DocClass.ob_type = &PyType_Type;
+
pdfdriver = gfxsource_pdf_create();
+ swfdriver = gfxsource_swf_create();
+ imagedriver = gfxsource_image_create();
- PyObject*module = Py_InitModule("gfx", pdf2swf_methods);
+ PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
+ PyObject*module_dict = PyModule_GetDict(module);
+
+ PyDict_SetItemString(module_dict, "Doc", (PyObject*)&DocClass);
+ PyDict_SetItemString(module_dict, "Page", (PyObject*)&PageClass);
+ PyDict_SetItemString(module_dict, "Output", (PyObject*)&OutputClass);
}