3 Python wrapper for gfx convert
5 Part of the swftools package.
7 Copyright (c) 2003 Matthias Kramm <kramm@quiss.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
26 #include "../../config.h"
27 #include "../gfxtools.h"
28 #include "../devices/swf.h"
29 #include "../devices/render.h"
30 #include "../devices/ocr.h"
31 #include "../devices/rescale.h"
32 #include "../devices/text.h"
34 #include "../devices/opengl.h"
36 #include "../pdf/pdf.h"
37 #include "../readers/swf.h"
38 #include "../readers/image.h"
42 static gfxsource_t*pdfdriver = 0;
43 static gfxsource_t*swfdriver = 0;
44 static gfxsource_t*imagedriver = 0;
46 staticforward PyTypeObject OutputClass;
47 staticforward PyTypeObject PageClass;
48 staticforward PyTypeObject DocClass;
52 gfxdevice_t*output_device;
53 PyObject*pyobj; //only for passthrough
69 static char* strf(char*format, ...)
74 va_start(arglist, format);
75 vsprintf(buf, format, arglist);
79 #define PY_ERROR(s,args...) (PyErr_SetString(PyExc_Exception, strf(s, ## args)),NULL)
80 #define PY_NONE Py_BuildValue("s", 0)
82 //---------------------------------------------------------------------
83 PyDoc_STRVAR(output_save_doc, \
85 "Saves the contents of an output device to a file\n"
86 "Depending on what the output device is, the contents\n"
87 "of the file may be plain text, an image, an SWF file,\n"
89 "For the ImageList device, several files (named\n"
90 "filename.1.png, filename.2.png etc.) might be created)\n"
92 static PyObject* output_save(PyObject* _self, PyObject* args, PyObject* kwargs)
94 OutputObject* self = (OutputObject*)_self;
96 static char *kwlist[] = {"filename", NULL};
97 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
100 gfxresult_t*result = self->output_device->finish(self->output_device);
101 self->output_device = 0;
102 if(result->save(result, filename) < 0) {
103 return PY_ERROR("Couldn't write to %s", filename);
105 result->destroy(result);
109 PyDoc_STRVAR(output_startpage_doc, \
110 "startpage(width, height)\n\n"
111 "Starts a new page/frame in the output device.\n"
112 "The usual way to render documents is to start a new page in the\n"
113 "device for each page in the document:\n"
115 "for pagenr in range(1,doc.pages+1):\n"
116 " page = doc.getPage(pagenr)\n"
117 " output.startpage(page.width, page.height)\n"
118 " page.render(output)\n"
119 " output.endpage()\n"
121 "It is, however, also possible to render more than one document page\n"
122 "to a single output page. E.g. for side-by-side or book views.\n"
124 static PyObject* output_startpage(PyObject* _self, PyObject* args, PyObject* kwargs)
126 OutputObject* self = (OutputObject*)_self;
127 int width=0, height=0;
128 if (!PyArg_ParseTuple(args, "ii", &width, &height))
130 self->output_device->startpage(self->output_device, width, height);
134 /* as the definition of the python image type comes from another module (not
135 included here, reproduce the necessary structure and extract the image
136 without using the type definition */
142 static gfximage_t*toImage(PyObject*_bitmap)
144 if(!_bitmap || !_bitmap->ob_type->tp_name || strcmp(_bitmap->ob_type->tp_name, "Image")) {
145 return PY_ERROR("Second argument to fillbitmap must be an image");
147 ImageObject*bitmap = (ImageObject*)_bitmap;
148 return bitmap->image;
151 static gfxline_t*toLine(PyObject*_line)
154 int num = PyList_Size(_line);
157 gfxline_t*last=&first;
159 PyObject*p= PySequence_GetItem(_line, t);
160 if(!PyTuple_Check(p))
161 return PY_ERROR("each point must be a tuple");
162 PyObject*_type = PyTuple_GetItem(p, 0);
163 if(!PyString_Check(_type))
164 return PY_ERROR("point tuples must start with a string");
165 char*type = PyString_AsString(_type);
167 int size = PyTuple_Size(p);
168 for(s=1;s<size;s++) {
169 if(!PyFloat_Check(PyTuple_GetItem(p,s))) {
170 return PY_ERROR("coordinates must be floats");
173 gfxline_t*l = malloc(sizeof(gfxline_t));
174 memset(l, 0, sizeof(gfxline_t));
178 l->type = gfx_moveTo;
180 return PY_ERROR("need 2 values for move");
181 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
182 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
183 } else if(type[0]=='l') {
184 l->type = gfx_lineTo;
186 return PY_ERROR("need 2 values for line");
187 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
188 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
189 } else if(type[0]=='s') {
190 l->type = gfx_splineTo;
192 return PY_ERROR("need 4 values for spline");
193 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
194 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
195 l->sx = PyFloat_AsDouble(PyTuple_GetItem(p, 3));
196 l->sy = PyFloat_AsDouble(PyTuple_GetItem(p, 4));
198 PY_ERROR("Unknown line code '%s'", l->type);
204 PyDoc_STRVAR(output_fillbitmap_doc, \
206 "fill a polygon with a bitmap pattern\n"
208 static PyObject* output_fillbitmap(PyObject* _self, PyObject* args, PyObject* kwargs)
210 OutputObject* self = (OutputObject*)_self;
213 static char *kwlist[] = {"line", "bitmap", NULL};
215 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", kwlist, &PyList_Type, &_line, &_bitmap))
218 gfximage_t*image = toImage(_bitmap);
222 gfxline_t*line = toLine(_line);
228 memset(&m, 0, sizeof(gfxmatrix_t));
231 self->output_device->fillbitmap(self->output_device, line, image, &m, 0);
236 PyDoc_STRVAR(output_fill_doc, \
238 "fill a polygon with a color\n"
240 static PyObject* output_fill(PyObject* _self, PyObject* args, PyObject* kwargs)
242 OutputObject* self = (OutputObject*)_self;
245 static char *kwlist[] = {"line", "color", NULL};
249 int a=255,r=0,g=0,b=0;
250 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", kwlist, &PyList_Type, &_line, &color))
253 if(!PyArg_ParseTuple(color, "iiii:color", &a, &r, &g, &b)) {
258 c.r = r; c.g = g; c.b = b; c.a = a;
260 gfxline_t*line = toLine(_line);
266 memset(&m, 0, sizeof(gfxmatrix_t));
269 self->output_device->fill(self->output_device, line, &c);
274 PyDoc_STRVAR(output_stroke_doc, \
276 "stroke a polygon with a color\n"
278 static PyObject* output_stroke(PyObject* _self, PyObject* args, PyObject* kwargs)
280 OutputObject* self = (OutputObject*)_self;
283 static char *kwlist[] = {"line", "width", "color", NULL};
287 int a=255,r=0,g=0,b=0;
289 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!fO", kwlist, &PyList_Type, &_line, &width, &color))
292 if(!PyArg_ParseTuple(color, "iiii:color", &a, &r, &g, &b)) {
297 c.r = r; c.g = g; c.b = b; c.a = a;
299 gfxline_t*line = toLine(_line);
303 self->output_device->stroke(self->output_device, line, width, &c,
304 /*TODO*/ gfx_capRound, gfx_joinRound, 0.0);
309 PyDoc_STRVAR(output_endpage_doc, \
311 "Ends a page in the output device. This function should be called\n"
312 "once for every startpage()\n"
314 static PyObject* output_endpage(PyObject* _self, PyObject* args, PyObject* kwargs)
316 OutputObject* self = (OutputObject*)_self;
317 if (!PyArg_ParseTuple(args, ""))
319 self->output_device->endpage(self->output_device);
322 PyDoc_STRVAR(output_setparameter_doc, \
323 "setparameter(key, value)\n\n"
324 "Set a output-device dependent parameter"
326 static PyObject* output_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
328 OutputObject* self = (OutputObject*)_self;
329 static char *kwlist[] = {"key", "value", NULL};
331 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
333 self->output_device->setparameter(self->output_device, key, value);
336 PyDoc_STRVAR(f_createSWF_doc, \
338 "Creates a device which renders documents to SWF (Flash) files.\n"
339 "Depending on the way the document parser behaves (see the poly2bitmap\n"
340 "and bitmap parameters), the resulting SWF might use vector operations\n"
341 "and Flash Texts to display the document, or just a single bitmap.\n"
343 static PyObject* f_createSWF(PyObject* parent, PyObject* args, PyObject* kwargs)
345 static char *kwlist[] = {NULL};
346 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
348 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
350 self->output_device = malloc(sizeof(gfxdevice_t));
351 gfxdevice_swf_init(self->output_device);
352 return (PyObject*)self;
355 PyDoc_STRVAR(f_createOCR_doc, \
357 "Creates a device which processes documents using OCR (optical\n"
358 "character recognition).\n"
359 "This is handy for e.g. extracting fulltext from PDF documents\n"
360 "which have broken fonts, and where hence the \"PlainText\"\n"
361 "device doesn't work.\n"
363 static PyObject* f_createOCR(PyObject* parent, PyObject* args, PyObject* kwargs)
365 static char *kwlist[] = {NULL};
366 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
368 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
370 self->output_device = malloc(sizeof(gfxdevice_t));
371 gfxdevice_ocr_init(self->output_device);
372 return (PyObject*)self;
376 PyDoc_STRVAR(f_createImageList_doc, \
378 "Creates a device which renders documents to bitmaps.\n"
379 "Each page that is rendered will create new bitmap.\n"
380 "Using save(), you can save the images to a number\n"
383 static PyObject* f_createImageList(PyObject* parent, PyObject* args, PyObject* kwargs)
385 static char *kwlist[] = {NULL};
386 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
388 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
390 self->output_device = malloc(sizeof(gfxdevice_t));
391 gfxdevice_render_init(self->output_device);
392 return (PyObject*)self;
395 PyDoc_STRVAR(f_createPlainText_doc, \
397 "Creates a device which can be used to extract text from documents,\n"
398 "by passing it as parameter to page.render().\n"
399 "The extracted text can be saved by plaintext.save(filename).\n"
401 static PyObject* f_createPlainText(PyObject* parent, PyObject* args, PyObject* kwargs)
403 static char *kwlist[] = {NULL};
404 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
406 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
408 self->output_device = malloc(sizeof(gfxdevice_t));
409 gfxdevice_text_init(self->output_device);
410 return (PyObject*)self;
414 PyDoc_STRVAR(f_createOpenGL_doc, \
416 "Creates a device which renders everything to OpenGL.\n"
417 "Can be used for desktop display and debugging.\n"
418 "This device is not available on all systems.\n"
420 static PyObject* f_createOpenGL(PyObject* parent, PyObject* args, PyObject* kwargs)
422 static char *kwlist[] = {NULL};
423 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
425 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
427 self->output_device = malloc(sizeof(gfxdevice_t));
428 gfxdevice_opengl_init(self->output_device);
429 return (PyObject*)self;
433 static PyObject*callback_python(char*function, gfxdevice_t*dev, const char*format, ...)
435 OutputObject*self = (OutputObject*)dev->internal;
437 if(!PyObject_HasAttrString(self->pyobj, function))
441 va_start(ap, format);
443 PyObject*tuple = PyTuple_New(strlen(format));
446 char p = format[pos];
448 char*s = va_arg(ap, char*);
449 PyTuple_SetItem(tuple, pos, PyString_FromString(s));
451 int i = va_arg(ap, int);
452 PyTuple_SetItem(tuple, pos, PyInt_FromLong(i));
454 void* ptr = va_arg(ap, void*);
455 gfxcolor_t*col = (gfxcolor_t*)ptr;
456 PyObject*colobj = PyTuple_New(4);
457 PyTuple_SetItem(colobj, 0, PyInt_FromLong(col->a));
458 PyTuple_SetItem(colobj, 1, PyInt_FromLong(col->r));
459 PyTuple_SetItem(colobj, 2, PyInt_FromLong(col->g));
460 PyTuple_SetItem(colobj, 3, PyInt_FromLong(col->b));
461 PyTuple_SetItem(tuple, pos, colobj);
463 void* ptr = va_arg(ap, void*);
464 gfxline_t*line = (gfxline_t*)ptr;
468 while(l) {l=l->next;len++;}
469 PyObject*list = PyList_New(len);
473 if(l->type == gfx_moveTo) {
474 point = PyTuple_New(3);
475 PyTuple_SetItem(point, 0, PyString_FromString("m"));
476 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
477 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
478 } else if(l->type == gfx_lineTo) {
479 point = PyTuple_New(3);
480 PyTuple_SetItem(point, 0, PyString_FromString("l"));
481 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
482 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
483 } else if(l->type == gfx_splineTo) {
484 point = PyTuple_New(5);
485 PyTuple_SetItem(point, 0, PyString_FromString("s"));
486 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
487 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
488 PyTuple_SetItem(point, 3, PyFloat_FromDouble(l->sx));
489 PyTuple_SetItem(point, 4, PyFloat_FromDouble(l->sy));
493 PyList_SetItem(list, i, point);
497 PyTuple_SetItem(tuple, pos, list);
499 PyTuple_SetItem(tuple, pos, PY_NONE);
504 PyObject*f = PyObject_GetAttrString(self->pyobj, function);
508 PyObject* result = PyObject_CallObject(f, tuple);
520 static int my_setparameter(gfxdevice_t*dev, const char*key, const char*value)
522 callback_python("setparameter", dev, "ss", key, value);
525 static void my_startpage(gfxdevice_t*dev, int width, int height)
527 callback_python("startpage", dev, "ii", width, height);
529 static void my_startclip(gfxdevice_t*dev, gfxline_t*line)
531 callback_python("startclip", dev, "l", line);
533 static void my_endclip(gfxdevice_t*dev)
535 callback_python("endclip", dev, "");
537 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)
541 if(cap_style == gfx_capButt)
543 else if(cap_style == gfx_capRound)
545 else if(cap_style == gfx_capSquare)
547 if(joint_style == gfx_joinMiter)
549 else if(joint_style == gfx_joinRound)
551 else if(joint_style == gfx_joinBevel)
553 callback_python("stroke", dev, "licssi", line, width, color, cap, joint, miterLimit);
555 static void my_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
557 callback_python("fill", dev, "lc", line, color);
559 static void my_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*imgcoord2devcoord, gfxcxform_t*cxform)
561 callback_python("fillbitmap", dev, "lImx", line, img, imgcoord2devcoord, cxform);
563 static void my_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
565 callback_python("fillgradient", dev, "lgsm", line, gradient, type, matrix);
567 static void my_addfont(gfxdevice_t*dev, gfxfont_t*font)
569 callback_python("addfont", dev, "f", font);
571 static void my_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
573 callback_python("drawchar", dev, "ficm", font, glyph, color, matrix);
575 static void my_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
577 callback_python("drawlink", dev, "ls", line, action);
579 static void my_endpage(gfxdevice_t*dev)
581 callback_python("drawlink", dev, "");
583 static gfxresult_t* my_finish(gfxdevice_t*dev)
585 callback_python("finish", dev, "");
590 PyDoc_STRVAR(f_createPassThrough_doc, \
591 "PassThrough(device)\n\n"
592 "Creates a PassThrough device, which can be used as parameter in calls\n"
593 "to page.render().\n"
594 "device needs to be a class implementing at least the following functions:\n\n"
595 "setparameter(key,value)\n"
596 "startclip(outline)\n"
598 "stroke(outline, width, color, capstyle, jointstyle, miterLimit)\n"
599 "fill(outline, color)\n"
600 "fillbitmap(outline, image, matrix, colortransform)\n"
601 "fillgradient(outline, gradient, gradienttype, matrix)\n"
603 "drawchar(font, glyph, color, matrix)\n"
604 "drawlink(outline, url)\n"
605 "If any of these functions are not defined, a error message will be printed,\n"
606 "however the rendering process will *not* be aborted.\n"
608 static PyObject* f_createPassThrough(PyObject* parent, PyObject* args, PyObject* kwargs)
610 static char *kwlist[] = {"device", NULL};
612 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &obj))
614 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
618 self->output_device = malloc(sizeof(gfxdevice_t));
619 memset(self->output_device, 0, sizeof(gfxdevice_t));
620 self->output_device->name = strdup("passthrough");
622 self->output_device->setparameter = my_setparameter;
623 self->output_device->startpage = my_startpage;
624 self->output_device->startclip = my_startclip;
625 self->output_device->addfont = my_addfont;
626 self->output_device->endclip = my_endclip;
627 self->output_device->stroke = my_stroke;
628 self->output_device->fill = my_fill;
629 self->output_device->fillbitmap = my_fillbitmap;
630 self->output_device->fillgradient = my_fillgradient;
631 self->output_device->drawchar = my_drawchar;
632 self->output_device->drawlink = my_drawlink;
633 self->output_device->endpage = my_endpage;
634 self->output_device->finish = my_finish;
635 self->output_device->internal = self;
637 return (PyObject*)self;
640 static PyMethodDef output_methods[] =
642 /* Output functions */
643 {"save", (PyCFunction)output_save, METH_KEYWORDS, output_save_doc},
644 {"startpage", (PyCFunction)output_startpage, METH_KEYWORDS, output_startpage_doc},
645 {"fill", (PyCFunction)output_fill, METH_KEYWORDS, output_fill_doc},
646 {"fillbitmap", (PyCFunction)output_fillbitmap, METH_KEYWORDS, output_fillbitmap_doc},
647 {"stroke", (PyCFunction)output_stroke, METH_KEYWORDS, output_stroke_doc},
648 {"endpage", (PyCFunction)output_endpage, METH_KEYWORDS, output_endpage_doc},
649 {"setparameter", (PyCFunction)output_setparameter, METH_KEYWORDS, output_setparameter_doc},
653 static void output_dealloc(PyObject* _self) {
654 OutputObject* self = (OutputObject*)_self;
656 if(self->output_device) {
657 gfxresult_t*result = self->output_device->finish(self->output_device);
659 result->destroy(result);result=0;
661 self->output_device = 0;
666 static PyObject* output_getattr(PyObject * _self, char* a)
668 OutputObject*self = (OutputObject*)_self;
670 /* if(!strcmp(a, "x1")) {
671 return PyInt_FromLong(self->output_device->x1);
672 } else if(!strcmp(a, "y1")) {
673 return PyInt_FromLong(self->output_device->y1);
674 } else if(!strcmp(a, "x2")) {
675 return PyInt_FromLong(self->output_device->x2);
676 } else if(!strcmp(a, "y2")) {
677 return PyInt_FromLong(self->output_device->y2);
680 return Py_FindMethod(output_methods, _self, a);
682 static int output_setattr(PyObject * _self, char* a, PyObject * o)
684 OutputObject*self = (OutputObject*)_self;
685 if(!PyString_Check(o))
687 char*value = PyString_AsString(o);
688 self->output_device->setparameter(self->output_device, a, value);
691 static int output_print(PyObject * _self, FILE *fi, int flags)
693 OutputObject*self = (OutputObject*)_self;
694 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
698 //---------------------------------------------------------------------
699 staticforward PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs);
700 staticforward PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs);
702 PyDoc_STRVAR(page_render_doc, \
703 "render(output, move=(0,0), clip=None)\n\n"
704 "Renders a page to the rendering backend specified by the output\n"
705 "parameter. Rendering consists of calling a number of functions on the\n"
706 "output device, see the description of the \"PassThrough\" device.\n"
707 "The page may be shifted to a given position using the move parameter,\n"
708 "and may also be clipped to a specific size using the clip parameter.\n"
709 "The clipping operation is applied after the move operation.\n"
711 static PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs)
713 PageObject* self = (PageObject*)_self;
715 static char *kwlist[] = {"dev", "move", "clip", NULL};
716 OutputObject*output = 0;
719 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &OutputClass, &output,
725 int cx1=0,cy1=0,cx2=0,cy2=0;
728 if (!PyArg_ParseTuple(move, "ii", &x,&y))
732 if (!PyArg_ParseTuple(clip, "iiii", &cx1,&cy1,&cx2,&cy2))
736 if(x|y|cx1|cx2|cy1|cy2)
737 self->page->rendersection(self->page, output->output_device,x,y,cx1,cy1,cx2,cy2);
739 self->page->render(self->page, output->output_device);
743 PyDoc_STRVAR(page_asImage_doc, \
744 "asImage(width, height)\n\n"
745 "Creates a bitmap from a page. The bitmap will be returned as a string\n"
746 "containing RGB triplets. The bitmap will be rescaled to the specified width and\n"
747 "height. The aspect ratio of width and height doesn't need to be the same\n"
750 static PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs)
752 PageObject* self = (PageObject*)_self;
754 static char *kwlist[] = {"width", "height", NULL};
755 int width=0,height=0;
756 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &width, &height))
759 if(!width || !height) {
760 return PY_ERROR("invalid dimensions: %dx%d", width,height);
763 gfxdevice_t dev1,dev2;
764 gfxdevice_render_init(&dev1);
765 dev1.setparameter(&dev1, "antialise", "2");
766 gfxdevice_rescale_init(&dev2, &dev1, width, height, 0);
767 dev2.startpage(&dev2, self->page->width, self->page->height);
768 self->page->render(self->page, &dev2);
770 gfxresult_t*result = dev2.finish(&dev2);
771 gfximage_t*img = (gfximage_t*)result->get(result,"page0");
772 int l = img->width*img->height;
773 unsigned char*data = malloc(img->width*img->height*3);
775 for(t=0,s=0;t<l;s+=3,t++) {
776 data[s+0] = img->data[t].r;
777 data[s+1] = img->data[t].g;
778 data[s+2] = img->data[t].b;
780 result->destroy(result);
781 return PyString_FromStringAndSize((char*)data,img->width*img->height*3);
784 static PyMethodDef page_methods[] =
787 {"render", (PyCFunction)page_render, METH_KEYWORDS, page_render_doc},
788 {"asImage", (PyCFunction)page_asImage, METH_KEYWORDS, page_asImage_doc},
791 static void page_dealloc(PyObject* _self) {
792 PageObject* self = (PageObject*)_self;
794 self->page->destroy(self->page);
798 Py_DECREF(self->parent);
805 static PyObject* page_getattr(PyObject * _self, char* a)
807 PageObject*self = (PageObject*)_self;
809 if(!strcmp(a, "size")) {
810 return Py_BuildValue("(ii)", self->page->width, self->page->height);
811 } if(!strcmp(a, "doc")) {
812 Py_INCREF(self->parent);
814 } if(!strcmp(a, "nr")) {
815 return PyInt_FromLong(self->nr);
816 } else if(!strcmp(a, "width")) {
817 return PyInt_FromLong(self->page->width);
818 } else if(!strcmp(a, "height")) {
819 return PyInt_FromLong(self->page->height);
821 return Py_FindMethod(page_methods, _self, a);
824 static int page_setattr(PyObject * self, char* a, PyObject * o) {
827 static int page_print(PyObject * _self, FILE *fi, int flags)
829 PageObject*self = (PageObject*)_self;
830 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
834 //---------------------------------------------------------------------
836 PyDoc_STRVAR(doc_getPage_doc,
838 "Get one page from a document file. The nr parameter specifies\n"
839 "which page to retrieve. Counting starts at 1, so the first page\n"
840 "can be retrieved by\n"
841 " page = doc.getPage(1)\n"
843 "You can find out how many pages a document contains by querying\n"
844 "its pages field (doc.pages)\n"
846 static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
848 DocObject* self = (DocObject*)_self;
850 static char *kwlist[] = {"nr", NULL};
852 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pagenr))
855 PageObject*page = PyObject_New(PageObject, &PageClass);
856 page->page = self->doc->getpage(self->doc, pagenr);
858 page->parent = _self;
859 Py_INCREF(page->parent);
862 return PY_ERROR("Couldn't extract page %d", pagenr);
864 return (PyObject*)page;
867 PyDoc_STRVAR(doc_getInfo_doc,
869 "Retrieve some information about a document. For PDF files, key\n"
870 "can have the following values:\n\n"
871 "\"title\", \"subject\", \"keywords\", \"author\", \"creator\", \"producer\",\n"
872 "\"creationdate\", \"moddate\", \"linearized\", \"tagged\", \"encrypted\",\n"
873 "\"oktoprint\", \"oktocopy\", \"oktochange\", \"oktoaddnotes\", \"version\".\n\n"
874 "If the \"oktocopy\" digital rights management flag is set to \"no\", then the\n"
875 "pdf parser won't allow you to access the PDF file. Trying to extract pages\n"
876 "from it will raise an exception.\n"
878 static PyObject* doc_getInfo(PyObject* _self, PyObject* args, PyObject* kwargs)
880 DocObject* self = (DocObject*)_self;
882 static char *kwlist[] = {"key", NULL};
884 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &key))
887 char*s = self->doc->getinfo(self->doc, key);
888 return PyString_FromString(s);
891 PyDoc_STRVAR(doc_setparameter_doc,
892 "setparameter(key, value)\n\n"
893 "Pass a parameter or setting to the document parser. Unlike\n"
894 "the module level setparameter() function, the parameters set\n"
895 "using setparameter will only be valid for the object itself\n"
896 "during its lifetime.\n"
898 static PyObject* doc_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
900 DocObject* self = (DocObject*)_self;
902 static char *kwlist[] = {"key", "value", NULL};
903 char*key = 0, *value=0;
904 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key,&value))
907 self->doc->set_parameter(self->doc, key, value);
911 PyDoc_STRVAR(f_open_doc,
912 "open(type, filename) -> object\n\n"
913 "Open a PDF, SWF or image file. The type argument should be \"pdf\",\n"
914 "\"swf\" or \"image\" accordingly. It returns a doc object which can be\n"
915 "used to process the file contents.\n"
917 " doc = open(\"pdf\", \"document.pdf\")\n"
918 " doc = open(\"swf\", \"flashfile.swf\")\n"
919 " doc = open(\"image\", \"image.png\")\n"
920 "If the file could not be loaded, or is a encrypted PDF file without\n"
921 "a proper password specified, an exception is being raised.\n"
922 "If the filename argument contains a '|' char, everything behind\n"
923 "the '|' is treated as password used for opening the file.\n"
925 " doc = open(\"pdf\", \"document.pdf|mysecretpassword\")\n"
927 "Notice that for image files, the only supported file formats right now\n"
928 "are jpeg and png.\n"
930 static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
932 static char *kwlist[] = {"type", "filename", NULL};
935 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
936 static char *kwlist2[] = {"filename", NULL};
939 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist2, &filename))
943 DocObject*self = PyObject_New(DocObject, &DocClass);
945 if(!type) { //autodetect
946 type = "pdf"; //default
947 int l = strlen(filename);
949 if(filename[l-4]=='.') {
950 if(strchr("pP", filename[l-3]) && strchr("dD", filename[l-2]) && strchr("fF", filename[l-1]))
952 if(strchr("jJ", filename[l-3]) && strchr("pP", filename[l-2]) && strchr("gG", filename[l-1]))
954 if(strchr("pP", filename[l-3]) && strchr("nN", filename[l-2]) && strchr("gG", filename[l-1]))
956 if(strchr("sS", filename[l-3]) && strchr("wW", filename[l-2]) && strchr("fF", filename[l-1]))
958 } else if(filename[l-5]=='.') {
964 if(!strcmp(type,"pdf"))
965 self->doc = pdfdriver->open(pdfdriver,filename);
966 else if(!strcmp(type, "image") || !strcmp(type, "img"))
967 self->doc = imagedriver->open(imagedriver, filename);
968 else if(!strcmp(type, "swf") || !strcmp(type, "SWF"))
969 self->doc = swfdriver->open(imagedriver, filename);
971 return PY_ERROR("Unknown type %s", type);
975 return PY_ERROR("Couldn't open %s", filename);
977 self->filename = strdup(filename);
978 return (PyObject*)self;
981 static PyMethodDef doc_methods[] =
984 {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, doc_getPage_doc},
985 {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, doc_getInfo_doc},
986 {"setparameter", (PyCFunction)doc_setparameter, METH_KEYWORDS, doc_setparameter_doc},
990 static void doc_dealloc(PyObject* _self) {
991 DocObject* self = (DocObject*)_self;
993 self->doc->destroy(self->doc);
997 free(self->filename);self->filename=0;
1001 static PyObject* doc_getattr(PyObject * _self, char* a)
1003 DocObject*self = (DocObject*)_self;
1004 if(!strcmp(a, "pages")) {
1005 return PyInt_FromLong(self->doc->num_pages);
1007 if(!strcmp(a, "filename")) {
1008 return PyString_FromString(self->filename);
1010 return Py_FindMethod(doc_methods, _self, a);
1012 static int doc_setattr(PyObject * self, char* a, PyObject * o) {
1015 static int doc_print(PyObject * _self, FILE *fi, int flags)
1017 DocObject*self = (DocObject*)_self;
1018 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
1022 //---------------------------------------------------------------------
1024 PyDoc_STRVAR(output_doc,
1025 "An Output object can be used as parameter to the render()\n"
1026 "call of a page. It's not possible to create this type of\n"
1027 "object directly (i.e., from a class), however you can\n"
1028 "use a PassThrough() device to pass things over to Python.\n"
1029 "Examples for classes implementing the Output class are: \n"
1030 "ImageList, SWF, PlainText and PassThrough.\n"
1032 static PyTypeObject OutputClass =
1034 PyObject_HEAD_INIT(NULL)
1036 tp_name: "gfx.Output",
1037 tp_basicsize: sizeof(OutputObject),
1039 tp_dealloc: output_dealloc,
1040 tp_print: output_print,
1041 tp_getattr: output_getattr,
1042 tp_setattr: output_setattr,
1044 tp_methods: output_methods
1046 PyDoc_STRVAR(page_doc,
1047 "A Page object contains a single page of a document.\n"
1048 "page.width and page.height (or page.size) contain the\n"
1049 "page dimensions. page.nr is the number of the page, and\n"
1050 "page.doc is the parent document.\n"
1052 static PyTypeObject PageClass =
1054 PyObject_HEAD_INIT(NULL)
1056 tp_name: "gfx.Page",
1057 tp_basicsize: sizeof(PageObject),
1059 tp_dealloc: page_dealloc,
1060 tp_print: page_print,
1061 tp_getattr: page_getattr,
1062 tp_setattr: page_setattr,
1064 tp_methods: page_methods
1066 PyDoc_STRVAR(doc_doc,
1067 "A Doc object is used for storing a document (like a PDF).\n"
1068 "doc.pages contains the number of pages in the document,\n"
1069 "and doc.filename the name of the file the document was\n"
1070 "created (loaded) from. If the document was created from\n"
1071 "an image file, the number of pages is always 1\n"
1073 static PyTypeObject DocClass =
1075 PyObject_HEAD_INIT(NULL)
1078 tp_basicsize: sizeof(DocObject),
1080 tp_dealloc: doc_dealloc,
1081 tp_print: doc_print,
1082 tp_getattr: doc_getattr,
1083 tp_setattr: doc_setattr,
1085 tp_methods: doc_methods,
1088 //=====================================================================
1090 PyDoc_STRVAR(f_setparameter_doc, \
1091 "setparameter(key,value)\n\n"
1092 "Set a parameter in the gfx module (which might affect the PDF\n"
1093 "parser or any of the rendering backends). This is a parameter\n"
1094 "which would usually be passed with the \"-s\" option to pdf2swf.\n"
1095 "For a list of all parameters, see the output of\n"
1096 " pdf2swf -s help\n"
1098 " pdf2swf somefile.pdf -s help\n"
1101 static PyObject* f_setparameter(PyObject* self, PyObject* args, PyObject* kwargs)
1103 static char *kwlist[] = {"key", "value", NULL};
1104 char*key=0,*value=0;
1105 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
1107 pdfdriver->set_parameter(pdfdriver,key,value);
1111 PyDoc_STRVAR(f_verbose_doc, \
1112 "verbose(level)\n\n"
1113 "Set the logging verbosity of the gfx module. Log levels are:\n"
1114 "level=-1 Log nothing\n"
1115 "level=0 (fatal) Log only fatal errors\n"
1116 "level=1 (error) Log only fatal errors and errors\n"
1117 "level=2 (warn) Log all errors and warnings\n"
1118 "level=3 (notice) Log also some rudimentary data about the parsing/conversion\n"
1119 "level=4 (verbose) Log some additional parsing information\n"
1120 "level=5 (debug) Log debug statements\n"
1121 "level=6 (trace) Log extended debug statements\n"
1122 "All logging messages are written to stdout.\n"
1124 static PyObject* f_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
1126 static char *kwlist[] = {"val", NULL};
1128 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &val))
1130 setConsoleLogging(val);
1134 PyDoc_STRVAR(f_addfont_doc, \
1135 "addfont(filename)\n\n"
1136 "Passes an additional font file to the PDF parser. If a PDF contains\n"
1137 "external fonts (i.e. fonts which are not contained in the PDF itself)\n"
1138 "then the files added by addfont() will be searched.\n"
1141 static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
1143 static char *kwlist[] = {"filename", NULL};
1145 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
1147 pdfdriver->set_parameter(pdfdriver,"font", filename);
1151 PyDoc_STRVAR(f_addfontdir_doc, \
1152 "addfontdir(dirname)\n\n"
1153 "Passes a complete directory containing fonts to the PDF parser. Any\n"
1154 "font file within this directory might be used to resolve external fonts\n"
1157 static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
1159 static char *kwlist[] = {"filename", NULL};
1161 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
1163 pdfdriver->set_parameter(pdfdriver,"fontdir", filename);
1167 static PyMethodDef pdf2swf_methods[] =
1170 {"open", (PyCFunction)f_open, METH_KEYWORDS, f_open_doc},
1171 {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, f_addfont_doc},
1172 {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, f_addfontdir_doc},
1173 {"setparameter", (PyCFunction)f_setparameter, METH_KEYWORDS, f_setparameter_doc},
1174 {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, f_verbose_doc},
1177 {"SWF", (PyCFunction)f_createSWF, METH_KEYWORDS, f_createSWF_doc},
1178 {"OCR", (PyCFunction)f_createOCR, METH_KEYWORDS, f_createOCR_doc},
1179 {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, f_createImageList_doc},
1180 {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, f_createPlainText_doc},
1181 {"PassThrough", (PyCFunction)f_createPassThrough, METH_KEYWORDS, f_createPassThrough_doc},
1183 {"OpenGL", (PyCFunction)f_createOpenGL, METH_KEYWORDS, f_createOpenGL_doc},
1190 PyDoc_STRVAR(gfx_doc, \
1191 "This module contains a PDF parser (based on xpdf) and a number of\n"
1192 "rendering backends. In particular, it can extract text from PDF pages,\n"
1193 "create bitmaps from them, or convert PDF files to SWF.\n"
1194 "The latter functionality is similar to what is offered by swftools'\n"
1195 "(http://www.swftools.org) pdf2swf utility, however more powerful-\n"
1196 "You can also create individual SWF files from single pages of the PDF\n"
1197 "or mix pages from different PDF files.\n"
1202 initLog(0,0,0,0,0,2);
1203 OutputClass.ob_type = &PyType_Type;
1204 PageClass.ob_type = &PyType_Type;
1205 DocClass.ob_type = &PyType_Type;
1207 pdfdriver = gfxsource_pdf_create();
1208 swfdriver = gfxsource_swf_create();
1209 imagedriver = gfxsource_image_create();
1211 PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
1212 PyObject*module_dict = PyModule_GetDict(module);
1214 PyDict_SetItemString(module_dict, "Doc", (PyObject*)&DocClass);
1215 PyDict_SetItemString(module_dict, "Page", (PyObject*)&PageClass);
1216 PyDict_SetItemString(module_dict, "Output", (PyObject*)&OutputClass);