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)),(PyObject*)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 PY_ERROR("Second argument to fillbitmap must be an image");
148 ImageObject*bitmap = (ImageObject*)_bitmap;
149 return bitmap->image;
152 static gfxline_t*toLine(PyObject*_line)
155 int num = PyList_Size(_line);
158 gfxline_t*last=&first;
160 PyObject*p= PySequence_GetItem(_line, t);
161 if(!PyTuple_Check(p))
162 return PY_ERROR("each point must be a tuple");
163 PyObject*_type = PyTuple_GetItem(p, 0);
164 if(!PyString_Check(_type))
165 return PY_ERROR("point tuples must start with a string");
166 char*type = PyString_AsString(_type);
168 int size = PyTuple_Size(p);
169 for(s=1;s<size;s++) {
170 if(!PyFloat_Check(PyTuple_GetItem(p,s))) {
171 return PY_ERROR("coordinates must be floats");
174 gfxline_t*l = (gfxline_t*)malloc(sizeof(gfxline_t));
175 memset(l, 0, sizeof(gfxline_t));
179 l->type = gfx_moveTo;
181 return PY_ERROR("need 2 values for move");
182 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
183 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
184 } else if(type[0]=='l') {
185 l->type = gfx_lineTo;
187 return PY_ERROR("need 2 values for line");
188 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
189 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
190 } else if(type[0]=='s') {
191 l->type = gfx_splineTo;
193 return PY_ERROR("need 4 values for spline");
194 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
195 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
196 l->sx = PyFloat_AsDouble(PyTuple_GetItem(p, 3));
197 l->sy = PyFloat_AsDouble(PyTuple_GetItem(p, 4));
199 return PY_ERROR("Unknown line code '%s'", type);
205 PyDoc_STRVAR(output_fillbitmap_doc, \
207 "fill a polygon with a bitmap pattern\n"
209 static PyObject* output_fillbitmap(PyObject* _self, PyObject* args, PyObject* kwargs)
211 OutputObject* self = (OutputObject*)_self;
214 static char *kwlist[] = {"line", "bitmap", NULL};
216 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", kwlist, &PyList_Type, &_line, &_bitmap))
219 gfximage_t*image = toImage(_bitmap);
221 return PY_ERROR("invalid image");
223 gfxline_t*line = toLine(_line);
229 memset(&m, 0, sizeof(gfxmatrix_t));
232 self->output_device->fillbitmap(self->output_device, line, image, &m, 0);
237 PyDoc_STRVAR(output_fill_doc, \
239 "fill a polygon with a color\n"
241 static PyObject* output_fill(PyObject* _self, PyObject* args, PyObject* kwargs)
243 OutputObject* self = (OutputObject*)_self;
246 static char *kwlist[] = {"line", "color", NULL};
250 int a=255,r=0,g=0,b=0;
251 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", kwlist, &PyList_Type, &_line, &color))
254 if(!PyArg_ParseTuple(color, "iiii:color", &a, &r, &g, &b)) {
259 c.r = r; c.g = g; c.b = b; c.a = a;
261 gfxline_t*line = toLine(_line);
267 memset(&m, 0, sizeof(gfxmatrix_t));
270 self->output_device->fill(self->output_device, line, &c);
275 PyDoc_STRVAR(output_stroke_doc, \
277 "stroke a polygon with a color\n"
279 static PyObject* output_stroke(PyObject* _self, PyObject* args, PyObject* kwargs)
281 OutputObject* self = (OutputObject*)_self;
284 static char *kwlist[] = {"line", "width", "color", NULL};
288 int a=255,r=0,g=0,b=0;
290 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!fO", kwlist, &PyList_Type, &_line, &width, &color))
293 if(!PyArg_ParseTuple(color, "iiii:color", &a, &r, &g, &b)) {
298 c.r = r; c.g = g; c.b = b; c.a = a;
300 gfxline_t*line = toLine(_line);
304 self->output_device->stroke(self->output_device, line, width, &c,
305 /*TODO*/ gfx_capRound, gfx_joinRound, 0.0);
310 PyDoc_STRVAR(output_endpage_doc, \
312 "Ends a page in the output device. This function should be called\n"
313 "once for every startpage()\n"
315 static PyObject* output_endpage(PyObject* _self, PyObject* args, PyObject* kwargs)
317 OutputObject* self = (OutputObject*)_self;
318 if (!PyArg_ParseTuple(args, ""))
320 self->output_device->endpage(self->output_device);
323 PyDoc_STRVAR(output_setparameter_doc, \
324 "setparameter(key, value)\n\n"
325 "Set a output-device dependent parameter"
327 static PyObject* output_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
329 OutputObject* self = (OutputObject*)_self;
330 static char *kwlist[] = {"key", "value", NULL};
332 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
334 self->output_device->setparameter(self->output_device, key, value);
337 PyDoc_STRVAR(f_createSWF_doc, \
339 "Creates a device which renders documents to SWF (Flash) files.\n"
340 "Depending on the way the document parser behaves (see the poly2bitmap\n"
341 "and bitmap parameters), the resulting SWF might use vector operations\n"
342 "and Flash Texts to display the document, or just a single bitmap.\n"
344 static PyObject* f_createSWF(PyObject* parent, PyObject* args, PyObject* kwargs)
346 static char *kwlist[] = {NULL};
347 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
349 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
351 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
352 gfxdevice_swf_init(self->output_device);
353 return (PyObject*)self;
356 PyDoc_STRVAR(f_createOCR_doc, \
358 "Creates a device which processes documents using OCR (optical\n"
359 "character recognition).\n"
360 "This is handy for e.g. extracting fulltext from PDF documents\n"
361 "which have broken fonts, and where hence the \"PlainText\"\n"
362 "device doesn't work.\n"
364 static PyObject* f_createOCR(PyObject* parent, PyObject* args, PyObject* kwargs)
366 static char *kwlist[] = {NULL};
367 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
369 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
371 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
372 gfxdevice_ocr_init(self->output_device);
373 return (PyObject*)self;
377 PyDoc_STRVAR(f_createImageList_doc, \
379 "Creates a device which renders documents to bitmaps.\n"
380 "Each page that is rendered will create new bitmap.\n"
381 "Using save(), you can save the images to a number\n"
384 static PyObject* f_createImageList(PyObject* parent, PyObject* args, PyObject* kwargs)
386 static char *kwlist[] = {NULL};
387 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
389 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
391 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
392 gfxdevice_render_init(self->output_device);
393 return (PyObject*)self;
396 PyDoc_STRVAR(f_createPlainText_doc, \
398 "Creates a device which can be used to extract text from documents,\n"
399 "by passing it as parameter to page.render().\n"
400 "The extracted text can be saved by plaintext.save(filename).\n"
402 static PyObject* f_createPlainText(PyObject* parent, PyObject* args, PyObject* kwargs)
404 static char *kwlist[] = {NULL};
405 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
407 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
409 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
410 gfxdevice_text_init(self->output_device);
411 return (PyObject*)self;
415 PyDoc_STRVAR(f_createOpenGL_doc, \
417 "Creates a device which renders everything to OpenGL.\n"
418 "Can be used for desktop display and debugging.\n"
419 "This device is not available on all systems.\n"
421 static PyObject* f_createOpenGL(PyObject* parent, PyObject* args, PyObject* kwargs)
423 static char *kwlist[] = {NULL};
424 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
426 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
428 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
429 gfxdevice_opengl_init(self->output_device);
430 return (PyObject*)self;
434 static char callback_python(char*function, gfxdevice_t*dev, const char*format, ...)
436 OutputObject*self = (OutputObject*)dev->internal;
438 if(!PyObject_HasAttrString(self->pyobj, function))
442 va_start(ap, format);
444 PyObject*tuple = PyTuple_New(strlen(format));
447 char p = format[pos];
449 char*s = va_arg(ap, char*);
450 PyTuple_SetItem(tuple, pos, PyString_FromString(s));
452 int i = va_arg(ap, int);
453 PyTuple_SetItem(tuple, pos, PyInt_FromLong(i));
455 int i = va_arg(ap, double);
456 PyTuple_SetItem(tuple, pos, PyFloat_FromDouble(i));
458 void* ptr = va_arg(ap, void*);
459 gfxcolor_t*col = (gfxcolor_t*)ptr;
460 PyObject*colobj = PyTuple_New(4);
461 PyTuple_SetItem(colobj, 0, PyInt_FromLong(col->a));
462 PyTuple_SetItem(colobj, 1, PyInt_FromLong(col->r));
463 PyTuple_SetItem(colobj, 2, PyInt_FromLong(col->g));
464 PyTuple_SetItem(colobj, 3, PyInt_FromLong(col->b));
465 PyTuple_SetItem(tuple, pos, colobj);
467 void* ptr = va_arg(ap, void*);
468 gfxline_t*line = (gfxline_t*)ptr;
472 while(l) {l=l->next;len++;}
473 PyObject*list = PyList_New(len);
477 if(l->type == gfx_moveTo) {
478 point = PyTuple_New(3);
479 PyTuple_SetItem(point, 0, PyString_FromString("m"));
480 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
481 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
482 } else if(l->type == gfx_lineTo) {
483 point = PyTuple_New(3);
484 PyTuple_SetItem(point, 0, PyString_FromString("l"));
485 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
486 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
487 } else if(l->type == gfx_splineTo) {
488 point = PyTuple_New(5);
489 PyTuple_SetItem(point, 0, PyString_FromString("s"));
490 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
491 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
492 PyTuple_SetItem(point, 3, PyFloat_FromDouble(l->sx));
493 PyTuple_SetItem(point, 4, PyFloat_FromDouble(l->sy));
497 PyList_SetItem(list, i, point);
501 PyTuple_SetItem(tuple, pos, list);
503 PyTuple_SetItem(tuple, pos, PY_NONE);
508 PyObject*f = PyObject_GetAttrString(self->pyobj, function);
512 PyObject* result = PyObject_CallObject(f, tuple);
524 static int my_setparameter(gfxdevice_t*dev, const char*key, const char*value)
526 callback_python("setparameter", dev, "ss", key, value);
529 static void my_startpage(gfxdevice_t*dev, int width, int height)
531 callback_python("startpage", dev, "ii", width, height);
533 static void my_startclip(gfxdevice_t*dev, gfxline_t*line)
535 callback_python("startclip", dev, "l", line);
537 static void my_endclip(gfxdevice_t*dev)
539 callback_python("endclip", dev, "");
541 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)
545 if(cap_style == gfx_capButt)
547 else if(cap_style == gfx_capRound)
549 else if(cap_style == gfx_capSquare)
551 if(joint_style == gfx_joinMiter)
553 else if(joint_style == gfx_joinRound)
555 else if(joint_style == gfx_joinBevel)
557 callback_python("stroke", dev, "ldcssi", line, width, color, cap, joint, miterLimit);
559 static void my_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
561 callback_python("fill", dev, "lc", line, color);
563 static void my_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*imgcoord2devcoord, gfxcxform_t*cxform)
565 callback_python("fillbitmap", dev, "lImx", line, img, imgcoord2devcoord, cxform);
567 static void my_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
569 callback_python("fillgradient", dev, "lgsm", line, gradient, type, matrix);
571 static void my_addfont(gfxdevice_t*dev, gfxfont_t*font)
573 callback_python("addfont", dev, "f", font);
575 static void my_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
577 if(!callback_python("drawchar", dev, "ficm", font, glyphnr, color, matrix))
581 gfxglyph_t*glyph = &font->glyphs[glyphnr];
582 gfxline_t*line2 = gfxline_clone(glyph->line);
583 gfxline_transform(line2, matrix);
584 my_fill(dev, line2, color);
589 static void my_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
591 callback_python("drawlink", dev, "ls", line, action);
593 static void my_endpage(gfxdevice_t*dev)
595 callback_python("drawlink", dev, "");
597 static gfxresult_t* my_finish(gfxdevice_t*dev)
599 callback_python("finish", dev, "");
604 PyDoc_STRVAR(f_createPassThrough_doc, \
605 "PassThrough(device)\n\n"
606 "Creates a PassThrough device, which can be used as parameter in calls\n"
607 "to page.render().\n"
608 "device needs to be a class implementing at least the following functions:\n\n"
609 "setparameter(key,value)\n"
610 "startclip(outline)\n"
612 "stroke(outline, width, color, capstyle, jointstyle, miterLimit)\n"
613 "fill(outline, color)\n"
614 "fillbitmap(outline, image, matrix, colortransform)\n"
615 "fillgradient(outline, gradient, gradienttype, matrix)\n"
617 "drawchar(font, glyph, color, matrix)\n"
618 "drawlink(outline, url)\n"
619 "If any of these functions are not defined, a error message will be printed,\n"
620 "however the rendering process will *not* be aborted.\n"
622 static PyObject* f_createPassThrough(PyObject* parent, PyObject* args, PyObject* kwargs)
624 static char *kwlist[] = {"device", NULL};
626 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &obj))
628 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
632 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
633 memset(self->output_device, 0, sizeof(gfxdevice_t));
634 self->output_device->name = strdup("passthrough");
636 self->output_device->setparameter = my_setparameter;
637 self->output_device->startpage = my_startpage;
638 self->output_device->startclip = my_startclip;
639 self->output_device->addfont = my_addfont;
640 self->output_device->endclip = my_endclip;
641 self->output_device->stroke = my_stroke;
642 self->output_device->fill = my_fill;
643 self->output_device->fillbitmap = my_fillbitmap;
644 self->output_device->fillgradient = my_fillgradient;
645 self->output_device->drawchar = my_drawchar;
646 self->output_device->drawlink = my_drawlink;
647 self->output_device->endpage = my_endpage;
648 self->output_device->finish = my_finish;
649 self->output_device->internal = self;
651 return (PyObject*)self;
654 static PyMethodDef output_methods[] =
656 /* Output functions */
657 {"save", (PyCFunction)output_save, METH_KEYWORDS, output_save_doc},
658 {"startpage", (PyCFunction)output_startpage, METH_KEYWORDS, output_startpage_doc},
659 {"fill", (PyCFunction)output_fill, METH_KEYWORDS, output_fill_doc},
660 {"fillbitmap", (PyCFunction)output_fillbitmap, METH_KEYWORDS, output_fillbitmap_doc},
661 {"stroke", (PyCFunction)output_stroke, METH_KEYWORDS, output_stroke_doc},
662 {"endpage", (PyCFunction)output_endpage, METH_KEYWORDS, output_endpage_doc},
663 {"setparameter", (PyCFunction)output_setparameter, METH_KEYWORDS, output_setparameter_doc},
667 static void output_dealloc(PyObject* _self) {
668 OutputObject* self = (OutputObject*)_self;
670 if(self->output_device) {
671 gfxresult_t*result = self->output_device->finish(self->output_device);
673 result->destroy(result);result=0;
675 self->output_device = 0;
680 static PyObject* output_getattr(PyObject * _self, char* a)
682 OutputObject*self = (OutputObject*)_self;
684 /* if(!strcmp(a, "x1")) {
685 return PyInt_FromLong(self->output_device->x1);
686 } else if(!strcmp(a, "y1")) {
687 return PyInt_FromLong(self->output_device->y1);
688 } else if(!strcmp(a, "x2")) {
689 return PyInt_FromLong(self->output_device->x2);
690 } else if(!strcmp(a, "y2")) {
691 return PyInt_FromLong(self->output_device->y2);
694 return Py_FindMethod(output_methods, _self, a);
696 static int output_setattr(PyObject * _self, char* a, PyObject * o)
698 OutputObject*self = (OutputObject*)_self;
699 if(!PyString_Check(o))
701 char*value = PyString_AsString(o);
702 self->output_device->setparameter(self->output_device, a, value);
705 static int output_print(PyObject * _self, FILE *fi, int flags)
707 OutputObject*self = (OutputObject*)_self;
708 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
712 //---------------------------------------------------------------------
713 staticforward PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs);
714 staticforward PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs);
716 PyDoc_STRVAR(page_render_doc, \
717 "render(output, move=(0,0), clip=None)\n\n"
718 "Renders a page to the rendering backend specified by the output\n"
719 "parameter. Rendering consists of calling a number of functions on the\n"
720 "output device, see the description of the \"PassThrough\" device.\n"
721 "The page may be shifted to a given position using the move parameter,\n"
722 "and may also be clipped to a specific size using the clip parameter.\n"
723 "The clipping operation is applied after the move operation.\n"
725 static PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs)
727 PageObject* self = (PageObject*)_self;
729 static char *kwlist[] = {"dev", "move", "clip", NULL};
730 OutputObject*output = 0;
733 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &OutputClass, &output,
739 int cx1=0,cy1=0,cx2=0,cy2=0;
742 if (!PyArg_ParseTuple(move, "ii", &x,&y))
746 if (!PyArg_ParseTuple(clip, "iiii", &cx1,&cy1,&cx2,&cy2))
750 if(x|y|cx1|cx2|cy1|cy2)
751 self->page->rendersection(self->page, output->output_device,x,y,cx1,cy1,cx2,cy2);
753 self->page->render(self->page, output->output_device);
757 PyDoc_STRVAR(page_asImage_doc, \
758 "asImage(width, height)\n\n"
759 "Creates a bitmap from a page. The bitmap will be returned as a string\n"
760 "containing RGB triplets. The bitmap will be rescaled to the specified width and\n"
761 "height. The aspect ratio of width and height doesn't need to be the same\n"
764 static PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs)
766 PageObject* self = (PageObject*)_self;
768 static char *kwlist[] = {"width", "height", NULL};
769 int width=0,height=0;
770 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &width, &height))
773 if(!width || !height) {
774 return PY_ERROR("invalid dimensions: %dx%d", width,height);
777 gfxdevice_t dev1,dev2;
778 gfxdevice_render_init(&dev1);
779 dev1.setparameter(&dev1, "antialise", "2");
780 gfxdevice_rescale_init(&dev2, &dev1, width, height, 0);
781 dev2.startpage(&dev2, self->page->width, self->page->height);
782 self->page->render(self->page, &dev2);
784 gfxresult_t*result = dev2.finish(&dev2);
785 gfximage_t*img = (gfximage_t*)result->get(result,"page0");
786 int l = img->width*img->height;
787 unsigned char*data = (unsigned char*)malloc(img->width*img->height*3);
789 for(t=0,s=0;t<l;s+=3,t++) {
790 data[s+0] = img->data[t].r;
791 data[s+1] = img->data[t].g;
792 data[s+2] = img->data[t].b;
794 result->destroy(result);
795 return PyString_FromStringAndSize((char*)data,img->width*img->height*3);
798 static PyMethodDef page_methods[] =
801 {"render", (PyCFunction)page_render, METH_KEYWORDS, page_render_doc},
802 {"asImage", (PyCFunction)page_asImage, METH_KEYWORDS, page_asImage_doc},
805 static void page_dealloc(PyObject* _self) {
806 PageObject* self = (PageObject*)_self;
808 self->page->destroy(self->page);
812 Py_DECREF(self->parent);
819 static PyObject* page_getattr(PyObject * _self, char* a)
821 PageObject*self = (PageObject*)_self;
823 if(!strcmp(a, "size")) {
824 return Py_BuildValue("(ii)", self->page->width, self->page->height);
825 } if(!strcmp(a, "doc")) {
826 Py_INCREF(self->parent);
828 } if(!strcmp(a, "nr")) {
829 return PyInt_FromLong(self->nr);
830 } else if(!strcmp(a, "width")) {
831 return PyInt_FromLong(self->page->width);
832 } else if(!strcmp(a, "height")) {
833 return PyInt_FromLong(self->page->height);
835 return Py_FindMethod(page_methods, _self, a);
838 static int page_setattr(PyObject * self, char* a, PyObject * o) {
841 static int page_print(PyObject * _self, FILE *fi, int flags)
843 PageObject*self = (PageObject*)_self;
844 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
848 //---------------------------------------------------------------------
850 PyDoc_STRVAR(doc_getPage_doc,
852 "Get one page from a document file. The nr parameter specifies\n"
853 "which page to retrieve. Counting starts at 1, so the first page\n"
854 "can be retrieved by\n"
855 " page = doc.getPage(1)\n"
857 "You can find out how many pages a document contains by querying\n"
858 "its pages field (doc.pages)\n"
860 static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
862 DocObject* self = (DocObject*)_self;
864 static char *kwlist[] = {"nr", NULL};
866 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pagenr))
869 PageObject*page = PyObject_New(PageObject, &PageClass);
870 page->page = self->doc->getpage(self->doc, pagenr);
872 page->parent = _self;
873 Py_INCREF(page->parent);
876 return PY_ERROR("Couldn't extract page %d", pagenr);
878 return (PyObject*)page;
881 PyDoc_STRVAR(doc_getInfo_doc,
883 "Retrieve some information about a document. For PDF files, key\n"
884 "can have the following values:\n\n"
885 "\"title\", \"subject\", \"keywords\", \"author\", \"creator\", \"producer\",\n"
886 "\"creationdate\", \"moddate\", \"linearized\", \"tagged\", \"encrypted\",\n"
887 "\"oktoprint\", \"oktocopy\", \"oktochange\", \"oktoaddnotes\", \"version\".\n\n"
888 "If the \"oktocopy\" digital rights management flag is set to \"no\", then the\n"
889 "pdf parser won't allow you to access the PDF file. Trying to extract pages\n"
890 "from it will raise an exception.\n"
892 static PyObject* doc_getInfo(PyObject* _self, PyObject* args, PyObject* kwargs)
894 DocObject* self = (DocObject*)_self;
896 static char *kwlist[] = {"key", NULL};
898 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &key))
901 char*s = self->doc->getinfo(self->doc, key);
902 return PyString_FromString(s);
905 PyDoc_STRVAR(doc_setparameter_doc,
906 "setparameter(key, value)\n\n"
907 "Pass a parameter or setting to the document parser. Unlike\n"
908 "the module level setparameter() function, the parameters set\n"
909 "using setparameter will only be valid for the object itself\n"
910 "during its lifetime.\n"
912 static PyObject* doc_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
914 DocObject* self = (DocObject*)_self;
916 static char *kwlist[] = {"key", "value", NULL};
917 char*key = 0, *value=0;
918 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key,&value))
921 self->doc->set_parameter(self->doc, key, value);
925 PyDoc_STRVAR(f_open_doc,
926 "open(type, filename) -> object\n\n"
927 "Open a PDF, SWF or image file. The type argument should be \"pdf\",\n"
928 "\"swf\" or \"image\" accordingly. It returns a doc object which can be\n"
929 "used to process the file contents.\n"
931 " doc = open(\"pdf\", \"document.pdf\")\n"
932 " doc = open(\"swf\", \"flashfile.swf\")\n"
933 " doc = open(\"image\", \"image.png\")\n"
934 "If the file could not be loaded, or is a encrypted PDF file without\n"
935 "a proper password specified, an exception is being raised.\n"
936 "If the filename argument contains a '|' char, everything behind\n"
937 "the '|' is treated as password used for opening the file.\n"
939 " doc = open(\"pdf\", \"document.pdf|mysecretpassword\")\n"
941 "Notice that for image files, the only supported file formats right now\n"
942 "are jpeg and png.\n"
944 static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
946 static char *kwlist[] = {"type", "filename", NULL};
949 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
950 static char *kwlist2[] = {"filename", NULL};
953 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist2, &filename))
957 DocObject*self = PyObject_New(DocObject, &DocClass);
959 if(!type) { //autodetect
960 type = "pdf"; //default
961 int l = strlen(filename);
963 if(filename[l-4]=='.') {
964 if(strchr("pP", filename[l-3]) && strchr("dD", filename[l-2]) && strchr("fF", filename[l-1]))
966 if(strchr("jJ", filename[l-3]) && strchr("pP", filename[l-2]) && strchr("gG", filename[l-1]))
968 if(strchr("pP", filename[l-3]) && strchr("nN", filename[l-2]) && strchr("gG", filename[l-1]))
970 if(strchr("sS", filename[l-3]) && strchr("wW", filename[l-2]) && strchr("fF", filename[l-1]))
972 } else if(filename[l-5]=='.') {
978 if(!strcmp(type,"pdf"))
979 self->doc = pdfdriver->open(pdfdriver,filename);
980 else if(!strcmp(type, "image") || !strcmp(type, "img"))
981 self->doc = imagedriver->open(imagedriver, filename);
982 else if(!strcmp(type, "swf") || !strcmp(type, "SWF"))
983 self->doc = swfdriver->open(imagedriver, filename);
985 return PY_ERROR("Unknown type %s", type);
989 return PY_ERROR("Couldn't open %s", filename);
991 self->filename = strdup(filename);
992 return (PyObject*)self;
995 static PyMethodDef doc_methods[] =
998 {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, doc_getPage_doc},
999 {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, doc_getInfo_doc},
1000 {"setparameter", (PyCFunction)doc_setparameter, METH_KEYWORDS, doc_setparameter_doc},
1004 static void doc_dealloc(PyObject* _self) {
1005 DocObject* self = (DocObject*)_self;
1007 self->doc->destroy(self->doc);
1010 if(self->filename) {
1011 free(self->filename);self->filename=0;
1015 static PyObject* doc_getattr(PyObject * _self, char* a)
1017 DocObject*self = (DocObject*)_self;
1018 if(!strcmp(a, "pages")) {
1019 return PyInt_FromLong(self->doc->num_pages);
1021 if(!strcmp(a, "filename")) {
1022 return PyString_FromString(self->filename);
1024 return Py_FindMethod(doc_methods, _self, a);
1026 static int doc_setattr(PyObject * self, char* a, PyObject * o) {
1029 static int doc_print(PyObject * _self, FILE *fi, int flags)
1031 DocObject*self = (DocObject*)_self;
1032 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
1036 //---------------------------------------------------------------------
1038 PyDoc_STRVAR(output_doc,
1039 "An Output object can be used as parameter to the render()\n"
1040 "call of a page. It's not possible to create this type of\n"
1041 "object directly (i.e., from a class), however you can\n"
1042 "use a PassThrough() device to pass things over to Python.\n"
1043 "Examples for classes implementing the Output class are: \n"
1044 "ImageList, SWF, PlainText and PassThrough.\n"
1046 static PyTypeObject OutputClass =
1048 PyObject_HEAD_INIT(NULL)
1050 tp_name: "gfx.Output",
1051 tp_basicsize: sizeof(OutputObject),
1053 tp_dealloc: output_dealloc,
1054 tp_print: output_print,
1055 tp_getattr: output_getattr,
1056 tp_setattr: output_setattr,
1058 tp_methods: output_methods
1060 PyDoc_STRVAR(page_doc,
1061 "A Page object contains a single page of a document.\n"
1062 "page.width and page.height (or page.size) contain the\n"
1063 "page dimensions. page.nr is the number of the page, and\n"
1064 "page.doc is the parent document.\n"
1066 static PyTypeObject PageClass =
1068 PyObject_HEAD_INIT(NULL)
1070 tp_name: "gfx.Page",
1071 tp_basicsize: sizeof(PageObject),
1073 tp_dealloc: page_dealloc,
1074 tp_print: page_print,
1075 tp_getattr: page_getattr,
1076 tp_setattr: page_setattr,
1078 tp_methods: page_methods
1080 PyDoc_STRVAR(doc_doc,
1081 "A Doc object is used for storing a document (like a PDF).\n"
1082 "doc.pages contains the number of pages in the document,\n"
1083 "and doc.filename the name of the file the document was\n"
1084 "created (loaded) from. If the document was created from\n"
1085 "an image file, the number of pages is always 1\n"
1087 static PyTypeObject DocClass =
1089 PyObject_HEAD_INIT(NULL)
1092 tp_basicsize: sizeof(DocObject),
1094 tp_dealloc: doc_dealloc,
1095 tp_print: doc_print,
1096 tp_getattr: doc_getattr,
1097 tp_setattr: doc_setattr,
1099 tp_methods: doc_methods,
1102 //=====================================================================
1104 PyDoc_STRVAR(f_setparameter_doc, \
1105 "setparameter(key,value)\n\n"
1106 "Set a parameter in the gfx module (which might affect the PDF\n"
1107 "parser or any of the rendering backends). This is a parameter\n"
1108 "which would usually be passed with the \"-s\" option to pdf2swf.\n"
1109 "For a list of all parameters, see the output of\n"
1110 " pdf2swf -s help\n"
1112 " pdf2swf somefile.pdf -s help\n"
1115 static PyObject* f_setparameter(PyObject* self, PyObject* args, PyObject* kwargs)
1117 static char *kwlist[] = {"key", "value", NULL};
1118 char*key=0,*value=0;
1119 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
1121 pdfdriver->set_parameter(pdfdriver,key,value);
1125 PyDoc_STRVAR(f_verbose_doc, \
1126 "verbose(level)\n\n"
1127 "Set the logging verbosity of the gfx module. Log levels are:\n"
1128 "level=-1 Log nothing\n"
1129 "level=0 (fatal) Log only fatal errors\n"
1130 "level=1 (error) Log only fatal errors and errors\n"
1131 "level=2 (warn) Log all errors and warnings\n"
1132 "level=3 (notice) Log also some rudimentary data about the parsing/conversion\n"
1133 "level=4 (verbose) Log some additional parsing information\n"
1134 "level=5 (debug) Log debug statements\n"
1135 "level=6 (trace) Log extended debug statements\n"
1136 "All logging messages are written to stdout.\n"
1138 static PyObject* f_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
1140 static char *kwlist[] = {"val", NULL};
1142 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &val))
1144 setConsoleLogging(val);
1148 PyDoc_STRVAR(f_addfont_doc, \
1149 "addfont(filename)\n\n"
1150 "Passes an additional font file to the PDF parser. If a PDF contains\n"
1151 "external fonts (i.e. fonts which are not contained in the PDF itself)\n"
1152 "then the files added by addfont() will be searched.\n"
1155 static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
1157 static char *kwlist[] = {"filename", NULL};
1159 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
1161 pdfdriver->set_parameter(pdfdriver,"font", filename);
1165 PyDoc_STRVAR(f_addfontdir_doc, \
1166 "addfontdir(dirname)\n\n"
1167 "Passes a complete directory containing fonts to the PDF parser. Any\n"
1168 "font file within this directory might be used to resolve external fonts\n"
1171 static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
1173 static char *kwlist[] = {"filename", NULL};
1175 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
1177 pdfdriver->set_parameter(pdfdriver,"fontdir", filename);
1181 static PyMethodDef pdf2swf_methods[] =
1184 {"open", (PyCFunction)f_open, METH_KEYWORDS, f_open_doc},
1185 {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, f_addfont_doc},
1186 {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, f_addfontdir_doc},
1187 {"setparameter", (PyCFunction)f_setparameter, METH_KEYWORDS, f_setparameter_doc},
1188 {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, f_verbose_doc},
1191 {"SWF", (PyCFunction)f_createSWF, METH_KEYWORDS, f_createSWF_doc},
1192 {"OCR", (PyCFunction)f_createOCR, METH_KEYWORDS, f_createOCR_doc},
1193 {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, f_createImageList_doc},
1194 {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, f_createPlainText_doc},
1195 {"PassThrough", (PyCFunction)f_createPassThrough, METH_KEYWORDS, f_createPassThrough_doc},
1197 {"OpenGL", (PyCFunction)f_createOpenGL, METH_KEYWORDS, f_createOpenGL_doc},
1204 PyDoc_STRVAR(gfx_doc, \
1205 "This module contains a PDF parser (based on xpdf) and a number of\n"
1206 "rendering backends. In particular, it can extract text from PDF pages,\n"
1207 "create bitmaps from them, or convert PDF files to SWF.\n"
1208 "The latter functionality is similar to what is offered by swftools'\n"
1209 "(http://www.swftools.org) pdf2swf utility, however more powerful-\n"
1210 "You can also create individual SWF files from single pages of the PDF\n"
1211 "or mix pages from different PDF files.\n"
1216 initLog(0,0,0,0,0,2);
1217 OutputClass.ob_type = &PyType_Type;
1218 PageClass.ob_type = &PyType_Type;
1219 DocClass.ob_type = &PyType_Type;
1221 pdfdriver = gfxsource_pdf_create();
1222 swfdriver = gfxsource_swf_create();
1223 imagedriver = gfxsource_image_create();
1225 PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
1226 PyObject*module_dict = PyModule_GetDict(module);
1228 PyDict_SetItemString(module_dict, "Doc", (PyObject*)&DocClass);
1229 PyDict_SetItemString(module_dict, "Page", (PyObject*)&PageClass);
1230 PyDict_SetItemString(module_dict, "Output", (PyObject*)&OutputClass);