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 PyObject*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 void* ptr = va_arg(ap, void*);
456 gfxcolor_t*col = (gfxcolor_t*)ptr;
457 PyObject*colobj = PyTuple_New(4);
458 PyTuple_SetItem(colobj, 0, PyInt_FromLong(col->a));
459 PyTuple_SetItem(colobj, 1, PyInt_FromLong(col->r));
460 PyTuple_SetItem(colobj, 2, PyInt_FromLong(col->g));
461 PyTuple_SetItem(colobj, 3, PyInt_FromLong(col->b));
462 PyTuple_SetItem(tuple, pos, colobj);
464 void* ptr = va_arg(ap, void*);
465 gfxline_t*line = (gfxline_t*)ptr;
469 while(l) {l=l->next;len++;}
470 PyObject*list = PyList_New(len);
474 if(l->type == gfx_moveTo) {
475 point = PyTuple_New(3);
476 PyTuple_SetItem(point, 0, PyString_FromString("m"));
477 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
478 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
479 } else if(l->type == gfx_lineTo) {
480 point = PyTuple_New(3);
481 PyTuple_SetItem(point, 0, PyString_FromString("l"));
482 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
483 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
484 } else if(l->type == gfx_splineTo) {
485 point = PyTuple_New(5);
486 PyTuple_SetItem(point, 0, PyString_FromString("s"));
487 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
488 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
489 PyTuple_SetItem(point, 3, PyFloat_FromDouble(l->sx));
490 PyTuple_SetItem(point, 4, PyFloat_FromDouble(l->sy));
494 PyList_SetItem(list, i, point);
498 PyTuple_SetItem(tuple, pos, list);
500 PyTuple_SetItem(tuple, pos, PY_NONE);
505 PyObject*f = PyObject_GetAttrString(self->pyobj, function);
509 PyObject* result = PyObject_CallObject(f, tuple);
521 static int my_setparameter(gfxdevice_t*dev, const char*key, const char*value)
523 callback_python("setparameter", dev, "ss", key, value);
526 static void my_startpage(gfxdevice_t*dev, int width, int height)
528 callback_python("startpage", dev, "ii", width, height);
530 static void my_startclip(gfxdevice_t*dev, gfxline_t*line)
532 callback_python("startclip", dev, "l", line);
534 static void my_endclip(gfxdevice_t*dev)
536 callback_python("endclip", dev, "");
538 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)
542 if(cap_style == gfx_capButt)
544 else if(cap_style == gfx_capRound)
546 else if(cap_style == gfx_capSquare)
548 if(joint_style == gfx_joinMiter)
550 else if(joint_style == gfx_joinRound)
552 else if(joint_style == gfx_joinBevel)
554 callback_python("stroke", dev, "licssi", line, width, color, cap, joint, miterLimit);
556 static void my_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
558 callback_python("fill", dev, "lc", line, color);
560 static void my_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*imgcoord2devcoord, gfxcxform_t*cxform)
562 callback_python("fillbitmap", dev, "lImx", line, img, imgcoord2devcoord, cxform);
564 static void my_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
566 callback_python("fillgradient", dev, "lgsm", line, gradient, type, matrix);
568 static void my_addfont(gfxdevice_t*dev, gfxfont_t*font)
570 callback_python("addfont", dev, "f", font);
572 static void my_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
574 callback_python("drawchar", dev, "ficm", font, glyph, color, matrix);
576 static void my_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
578 callback_python("drawlink", dev, "ls", line, action);
580 static void my_endpage(gfxdevice_t*dev)
582 callback_python("drawlink", dev, "");
584 static gfxresult_t* my_finish(gfxdevice_t*dev)
586 callback_python("finish", dev, "");
591 PyDoc_STRVAR(f_createPassThrough_doc, \
592 "PassThrough(device)\n\n"
593 "Creates a PassThrough device, which can be used as parameter in calls\n"
594 "to page.render().\n"
595 "device needs to be a class implementing at least the following functions:\n\n"
596 "setparameter(key,value)\n"
597 "startclip(outline)\n"
599 "stroke(outline, width, color, capstyle, jointstyle, miterLimit)\n"
600 "fill(outline, color)\n"
601 "fillbitmap(outline, image, matrix, colortransform)\n"
602 "fillgradient(outline, gradient, gradienttype, matrix)\n"
604 "drawchar(font, glyph, color, matrix)\n"
605 "drawlink(outline, url)\n"
606 "If any of these functions are not defined, a error message will be printed,\n"
607 "however the rendering process will *not* be aborted.\n"
609 static PyObject* f_createPassThrough(PyObject* parent, PyObject* args, PyObject* kwargs)
611 static char *kwlist[] = {"device", NULL};
613 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &obj))
615 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
619 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
620 memset(self->output_device, 0, sizeof(gfxdevice_t));
621 self->output_device->name = strdup("passthrough");
623 self->output_device->setparameter = my_setparameter;
624 self->output_device->startpage = my_startpage;
625 self->output_device->startclip = my_startclip;
626 self->output_device->addfont = my_addfont;
627 self->output_device->endclip = my_endclip;
628 self->output_device->stroke = my_stroke;
629 self->output_device->fill = my_fill;
630 self->output_device->fillbitmap = my_fillbitmap;
631 self->output_device->fillgradient = my_fillgradient;
632 self->output_device->drawchar = my_drawchar;
633 self->output_device->drawlink = my_drawlink;
634 self->output_device->endpage = my_endpage;
635 self->output_device->finish = my_finish;
636 self->output_device->internal = self;
638 return (PyObject*)self;
641 static PyMethodDef output_methods[] =
643 /* Output functions */
644 {"save", (PyCFunction)output_save, METH_KEYWORDS, output_save_doc},
645 {"startpage", (PyCFunction)output_startpage, METH_KEYWORDS, output_startpage_doc},
646 {"fill", (PyCFunction)output_fill, METH_KEYWORDS, output_fill_doc},
647 {"fillbitmap", (PyCFunction)output_fillbitmap, METH_KEYWORDS, output_fillbitmap_doc},
648 {"stroke", (PyCFunction)output_stroke, METH_KEYWORDS, output_stroke_doc},
649 {"endpage", (PyCFunction)output_endpage, METH_KEYWORDS, output_endpage_doc},
650 {"setparameter", (PyCFunction)output_setparameter, METH_KEYWORDS, output_setparameter_doc},
654 static void output_dealloc(PyObject* _self) {
655 OutputObject* self = (OutputObject*)_self;
657 if(self->output_device) {
658 gfxresult_t*result = self->output_device->finish(self->output_device);
660 result->destroy(result);result=0;
662 self->output_device = 0;
667 static PyObject* output_getattr(PyObject * _self, char* a)
669 OutputObject*self = (OutputObject*)_self;
671 /* if(!strcmp(a, "x1")) {
672 return PyInt_FromLong(self->output_device->x1);
673 } else if(!strcmp(a, "y1")) {
674 return PyInt_FromLong(self->output_device->y1);
675 } else if(!strcmp(a, "x2")) {
676 return PyInt_FromLong(self->output_device->x2);
677 } else if(!strcmp(a, "y2")) {
678 return PyInt_FromLong(self->output_device->y2);
681 return Py_FindMethod(output_methods, _self, a);
683 static int output_setattr(PyObject * _self, char* a, PyObject * o)
685 OutputObject*self = (OutputObject*)_self;
686 if(!PyString_Check(o))
688 char*value = PyString_AsString(o);
689 self->output_device->setparameter(self->output_device, a, value);
692 static int output_print(PyObject * _self, FILE *fi, int flags)
694 OutputObject*self = (OutputObject*)_self;
695 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
699 //---------------------------------------------------------------------
700 staticforward PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs);
701 staticforward PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs);
703 PyDoc_STRVAR(page_render_doc, \
704 "render(output, move=(0,0), clip=None)\n\n"
705 "Renders a page to the rendering backend specified by the output\n"
706 "parameter. Rendering consists of calling a number of functions on the\n"
707 "output device, see the description of the \"PassThrough\" device.\n"
708 "The page may be shifted to a given position using the move parameter,\n"
709 "and may also be clipped to a specific size using the clip parameter.\n"
710 "The clipping operation is applied after the move operation.\n"
712 static PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs)
714 PageObject* self = (PageObject*)_self;
716 static char *kwlist[] = {"dev", "move", "clip", NULL};
717 OutputObject*output = 0;
720 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &OutputClass, &output,
726 int cx1=0,cy1=0,cx2=0,cy2=0;
729 if (!PyArg_ParseTuple(move, "ii", &x,&y))
733 if (!PyArg_ParseTuple(clip, "iiii", &cx1,&cy1,&cx2,&cy2))
737 if(x|y|cx1|cx2|cy1|cy2)
738 self->page->rendersection(self->page, output->output_device,x,y,cx1,cy1,cx2,cy2);
740 self->page->render(self->page, output->output_device);
744 PyDoc_STRVAR(page_asImage_doc, \
745 "asImage(width, height)\n\n"
746 "Creates a bitmap from a page. The bitmap will be returned as a string\n"
747 "containing RGB triplets. The bitmap will be rescaled to the specified width and\n"
748 "height. The aspect ratio of width and height doesn't need to be the same\n"
751 static PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs)
753 PageObject* self = (PageObject*)_self;
755 static char *kwlist[] = {"width", "height", NULL};
756 int width=0,height=0;
757 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &width, &height))
760 if(!width || !height) {
761 return PY_ERROR("invalid dimensions: %dx%d", width,height);
764 gfxdevice_t dev1,dev2;
765 gfxdevice_render_init(&dev1);
766 dev1.setparameter(&dev1, "antialise", "2");
767 gfxdevice_rescale_init(&dev2, &dev1, width, height, 0);
768 dev2.startpage(&dev2, self->page->width, self->page->height);
769 self->page->render(self->page, &dev2);
771 gfxresult_t*result = dev2.finish(&dev2);
772 gfximage_t*img = (gfximage_t*)result->get(result,"page0");
773 int l = img->width*img->height;
774 unsigned char*data = (unsigned char*)malloc(img->width*img->height*3);
776 for(t=0,s=0;t<l;s+=3,t++) {
777 data[s+0] = img->data[t].r;
778 data[s+1] = img->data[t].g;
779 data[s+2] = img->data[t].b;
781 result->destroy(result);
782 return PyString_FromStringAndSize((char*)data,img->width*img->height*3);
785 static PyMethodDef page_methods[] =
788 {"render", (PyCFunction)page_render, METH_KEYWORDS, page_render_doc},
789 {"asImage", (PyCFunction)page_asImage, METH_KEYWORDS, page_asImage_doc},
792 static void page_dealloc(PyObject* _self) {
793 PageObject* self = (PageObject*)_self;
795 self->page->destroy(self->page);
799 Py_DECREF(self->parent);
806 static PyObject* page_getattr(PyObject * _self, char* a)
808 PageObject*self = (PageObject*)_self;
810 if(!strcmp(a, "size")) {
811 return Py_BuildValue("(ii)", self->page->width, self->page->height);
812 } if(!strcmp(a, "doc")) {
813 Py_INCREF(self->parent);
815 } if(!strcmp(a, "nr")) {
816 return PyInt_FromLong(self->nr);
817 } else if(!strcmp(a, "width")) {
818 return PyInt_FromLong(self->page->width);
819 } else if(!strcmp(a, "height")) {
820 return PyInt_FromLong(self->page->height);
822 return Py_FindMethod(page_methods, _self, a);
825 static int page_setattr(PyObject * self, char* a, PyObject * o) {
828 static int page_print(PyObject * _self, FILE *fi, int flags)
830 PageObject*self = (PageObject*)_self;
831 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
835 //---------------------------------------------------------------------
837 PyDoc_STRVAR(doc_getPage_doc,
839 "Get one page from a document file. The nr parameter specifies\n"
840 "which page to retrieve. Counting starts at 1, so the first page\n"
841 "can be retrieved by\n"
842 " page = doc.getPage(1)\n"
844 "You can find out how many pages a document contains by querying\n"
845 "its pages field (doc.pages)\n"
847 static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
849 DocObject* self = (DocObject*)_self;
851 static char *kwlist[] = {"nr", NULL};
853 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pagenr))
856 PageObject*page = PyObject_New(PageObject, &PageClass);
857 page->page = self->doc->getpage(self->doc, pagenr);
859 page->parent = _self;
860 Py_INCREF(page->parent);
863 return PY_ERROR("Couldn't extract page %d", pagenr);
865 return (PyObject*)page;
868 PyDoc_STRVAR(doc_getInfo_doc,
870 "Retrieve some information about a document. For PDF files, key\n"
871 "can have the following values:\n\n"
872 "\"title\", \"subject\", \"keywords\", \"author\", \"creator\", \"producer\",\n"
873 "\"creationdate\", \"moddate\", \"linearized\", \"tagged\", \"encrypted\",\n"
874 "\"oktoprint\", \"oktocopy\", \"oktochange\", \"oktoaddnotes\", \"version\".\n\n"
875 "If the \"oktocopy\" digital rights management flag is set to \"no\", then the\n"
876 "pdf parser won't allow you to access the PDF file. Trying to extract pages\n"
877 "from it will raise an exception.\n"
879 static PyObject* doc_getInfo(PyObject* _self, PyObject* args, PyObject* kwargs)
881 DocObject* self = (DocObject*)_self;
883 static char *kwlist[] = {"key", NULL};
885 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &key))
888 char*s = self->doc->getinfo(self->doc, key);
889 return PyString_FromString(s);
892 PyDoc_STRVAR(doc_setparameter_doc,
893 "setparameter(key, value)\n\n"
894 "Pass a parameter or setting to the document parser. Unlike\n"
895 "the module level setparameter() function, the parameters set\n"
896 "using setparameter will only be valid for the object itself\n"
897 "during its lifetime.\n"
899 static PyObject* doc_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
901 DocObject* self = (DocObject*)_self;
903 static char *kwlist[] = {"key", "value", NULL};
904 char*key = 0, *value=0;
905 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key,&value))
908 self->doc->set_parameter(self->doc, key, value);
912 PyDoc_STRVAR(f_open_doc,
913 "open(type, filename) -> object\n\n"
914 "Open a PDF, SWF or image file. The type argument should be \"pdf\",\n"
915 "\"swf\" or \"image\" accordingly. It returns a doc object which can be\n"
916 "used to process the file contents.\n"
918 " doc = open(\"pdf\", \"document.pdf\")\n"
919 " doc = open(\"swf\", \"flashfile.swf\")\n"
920 " doc = open(\"image\", \"image.png\")\n"
921 "If the file could not be loaded, or is a encrypted PDF file without\n"
922 "a proper password specified, an exception is being raised.\n"
923 "If the filename argument contains a '|' char, everything behind\n"
924 "the '|' is treated as password used for opening the file.\n"
926 " doc = open(\"pdf\", \"document.pdf|mysecretpassword\")\n"
928 "Notice that for image files, the only supported file formats right now\n"
929 "are jpeg and png.\n"
931 static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
933 static char *kwlist[] = {"type", "filename", NULL};
936 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
937 static char *kwlist2[] = {"filename", NULL};
940 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist2, &filename))
944 DocObject*self = PyObject_New(DocObject, &DocClass);
946 if(!type) { //autodetect
947 type = "pdf"; //default
948 int l = strlen(filename);
950 if(filename[l-4]=='.') {
951 if(strchr("pP", filename[l-3]) && strchr("dD", filename[l-2]) && strchr("fF", filename[l-1]))
953 if(strchr("jJ", filename[l-3]) && strchr("pP", filename[l-2]) && strchr("gG", filename[l-1]))
955 if(strchr("pP", filename[l-3]) && strchr("nN", filename[l-2]) && strchr("gG", filename[l-1]))
957 if(strchr("sS", filename[l-3]) && strchr("wW", filename[l-2]) && strchr("fF", filename[l-1]))
959 } else if(filename[l-5]=='.') {
965 if(!strcmp(type,"pdf"))
966 self->doc = pdfdriver->open(pdfdriver,filename);
967 else if(!strcmp(type, "image") || !strcmp(type, "img"))
968 self->doc = imagedriver->open(imagedriver, filename);
969 else if(!strcmp(type, "swf") || !strcmp(type, "SWF"))
970 self->doc = swfdriver->open(imagedriver, filename);
972 return PY_ERROR("Unknown type %s", type);
976 return PY_ERROR("Couldn't open %s", filename);
978 self->filename = strdup(filename);
979 return (PyObject*)self;
982 static PyMethodDef doc_methods[] =
985 {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, doc_getPage_doc},
986 {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, doc_getInfo_doc},
987 {"setparameter", (PyCFunction)doc_setparameter, METH_KEYWORDS, doc_setparameter_doc},
991 static void doc_dealloc(PyObject* _self) {
992 DocObject* self = (DocObject*)_self;
994 self->doc->destroy(self->doc);
998 free(self->filename);self->filename=0;
1002 static PyObject* doc_getattr(PyObject * _self, char* a)
1004 DocObject*self = (DocObject*)_self;
1005 if(!strcmp(a, "pages")) {
1006 return PyInt_FromLong(self->doc->num_pages);
1008 if(!strcmp(a, "filename")) {
1009 return PyString_FromString(self->filename);
1011 return Py_FindMethod(doc_methods, _self, a);
1013 static int doc_setattr(PyObject * self, char* a, PyObject * o) {
1016 static int doc_print(PyObject * _self, FILE *fi, int flags)
1018 DocObject*self = (DocObject*)_self;
1019 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
1023 //---------------------------------------------------------------------
1025 PyDoc_STRVAR(output_doc,
1026 "An Output object can be used as parameter to the render()\n"
1027 "call of a page. It's not possible to create this type of\n"
1028 "object directly (i.e., from a class), however you can\n"
1029 "use a PassThrough() device to pass things over to Python.\n"
1030 "Examples for classes implementing the Output class are: \n"
1031 "ImageList, SWF, PlainText and PassThrough.\n"
1033 static PyTypeObject OutputClass =
1035 PyObject_HEAD_INIT(NULL)
1037 tp_name: "gfx.Output",
1038 tp_basicsize: sizeof(OutputObject),
1040 tp_dealloc: output_dealloc,
1041 tp_print: output_print,
1042 tp_getattr: output_getattr,
1043 tp_setattr: output_setattr,
1045 tp_methods: output_methods
1047 PyDoc_STRVAR(page_doc,
1048 "A Page object contains a single page of a document.\n"
1049 "page.width and page.height (or page.size) contain the\n"
1050 "page dimensions. page.nr is the number of the page, and\n"
1051 "page.doc is the parent document.\n"
1053 static PyTypeObject PageClass =
1055 PyObject_HEAD_INIT(NULL)
1057 tp_name: "gfx.Page",
1058 tp_basicsize: sizeof(PageObject),
1060 tp_dealloc: page_dealloc,
1061 tp_print: page_print,
1062 tp_getattr: page_getattr,
1063 tp_setattr: page_setattr,
1065 tp_methods: page_methods
1067 PyDoc_STRVAR(doc_doc,
1068 "A Doc object is used for storing a document (like a PDF).\n"
1069 "doc.pages contains the number of pages in the document,\n"
1070 "and doc.filename the name of the file the document was\n"
1071 "created (loaded) from. If the document was created from\n"
1072 "an image file, the number of pages is always 1\n"
1074 static PyTypeObject DocClass =
1076 PyObject_HEAD_INIT(NULL)
1079 tp_basicsize: sizeof(DocObject),
1081 tp_dealloc: doc_dealloc,
1082 tp_print: doc_print,
1083 tp_getattr: doc_getattr,
1084 tp_setattr: doc_setattr,
1086 tp_methods: doc_methods,
1089 //=====================================================================
1091 PyDoc_STRVAR(f_setparameter_doc, \
1092 "setparameter(key,value)\n\n"
1093 "Set a parameter in the gfx module (which might affect the PDF\n"
1094 "parser or any of the rendering backends). This is a parameter\n"
1095 "which would usually be passed with the \"-s\" option to pdf2swf.\n"
1096 "For a list of all parameters, see the output of\n"
1097 " pdf2swf -s help\n"
1099 " pdf2swf somefile.pdf -s help\n"
1102 static PyObject* f_setparameter(PyObject* self, PyObject* args, PyObject* kwargs)
1104 static char *kwlist[] = {"key", "value", NULL};
1105 char*key=0,*value=0;
1106 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
1108 pdfdriver->set_parameter(pdfdriver,key,value);
1112 PyDoc_STRVAR(f_verbose_doc, \
1113 "verbose(level)\n\n"
1114 "Set the logging verbosity of the gfx module. Log levels are:\n"
1115 "level=-1 Log nothing\n"
1116 "level=0 (fatal) Log only fatal errors\n"
1117 "level=1 (error) Log only fatal errors and errors\n"
1118 "level=2 (warn) Log all errors and warnings\n"
1119 "level=3 (notice) Log also some rudimentary data about the parsing/conversion\n"
1120 "level=4 (verbose) Log some additional parsing information\n"
1121 "level=5 (debug) Log debug statements\n"
1122 "level=6 (trace) Log extended debug statements\n"
1123 "All logging messages are written to stdout.\n"
1125 static PyObject* f_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
1127 static char *kwlist[] = {"val", NULL};
1129 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &val))
1131 setConsoleLogging(val);
1135 PyDoc_STRVAR(f_addfont_doc, \
1136 "addfont(filename)\n\n"
1137 "Passes an additional font file to the PDF parser. If a PDF contains\n"
1138 "external fonts (i.e. fonts which are not contained in the PDF itself)\n"
1139 "then the files added by addfont() will be searched.\n"
1142 static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
1144 static char *kwlist[] = {"filename", NULL};
1146 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
1148 pdfdriver->set_parameter(pdfdriver,"font", filename);
1152 PyDoc_STRVAR(f_addfontdir_doc, \
1153 "addfontdir(dirname)\n\n"
1154 "Passes a complete directory containing fonts to the PDF parser. Any\n"
1155 "font file within this directory might be used to resolve external fonts\n"
1158 static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
1160 static char *kwlist[] = {"filename", NULL};
1162 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
1164 pdfdriver->set_parameter(pdfdriver,"fontdir", filename);
1168 static PyMethodDef pdf2swf_methods[] =
1171 {"open", (PyCFunction)f_open, METH_KEYWORDS, f_open_doc},
1172 {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, f_addfont_doc},
1173 {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, f_addfontdir_doc},
1174 {"setparameter", (PyCFunction)f_setparameter, METH_KEYWORDS, f_setparameter_doc},
1175 {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, f_verbose_doc},
1178 {"SWF", (PyCFunction)f_createSWF, METH_KEYWORDS, f_createSWF_doc},
1179 {"OCR", (PyCFunction)f_createOCR, METH_KEYWORDS, f_createOCR_doc},
1180 {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, f_createImageList_doc},
1181 {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, f_createPlainText_doc},
1182 {"PassThrough", (PyCFunction)f_createPassThrough, METH_KEYWORDS, f_createPassThrough_doc},
1184 {"OpenGL", (PyCFunction)f_createOpenGL, METH_KEYWORDS, f_createOpenGL_doc},
1191 PyDoc_STRVAR(gfx_doc, \
1192 "This module contains a PDF parser (based on xpdf) and a number of\n"
1193 "rendering backends. In particular, it can extract text from PDF pages,\n"
1194 "create bitmaps from them, or convert PDF files to SWF.\n"
1195 "The latter functionality is similar to what is offered by swftools'\n"
1196 "(http://www.swftools.org) pdf2swf utility, however more powerful-\n"
1197 "You can also create individual SWF files from single pages of the PDF\n"
1198 "or mix pages from different PDF files.\n"
1203 initLog(0,0,0,0,0,2);
1204 OutputClass.ob_type = &PyType_Type;
1205 PageClass.ob_type = &PyType_Type;
1206 DocClass.ob_type = &PyType_Type;
1208 pdfdriver = gfxsource_pdf_create();
1209 swfdriver = gfxsource_swf_create();
1210 imagedriver = gfxsource_image_create();
1212 PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
1213 PyObject*module_dict = PyModule_GetDict(module);
1215 PyDict_SetItemString(module_dict, "Doc", (PyObject*)&DocClass);
1216 PyDict_SetItemString(module_dict, "Page", (PyObject*)&PageClass);
1217 PyDict_SetItemString(module_dict, "Output", (PyObject*)&OutputClass);