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 vsnprintf(buf, sizeof(buf)-1, format, arglist);
79 #define PY_ERROR(s,args...) (PyErr_SetString(PyExc_Exception, strf(s, ## args)),(void*)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");
164 PyObject*_type = PyTuple_GetItem(p, 0);
165 if(!PyString_Check(_type))
166 return PY_ERROR("point tuples must start with a string");
167 char*type = PyString_AsString(_type);
169 int size = PyTuple_Size(p);
170 for(s=1;s<size;s++) {
171 if(!PyFloat_Check(PyTuple_GetItem(p,s))) {
172 return PY_ERROR("coordinates must be floats");
175 gfxline_t*l = (gfxline_t*)malloc(sizeof(gfxline_t));
176 memset(l, 0, sizeof(gfxline_t));
180 l->type = gfx_moveTo;
182 return PY_ERROR("need 2 values for move");
183 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
184 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
185 } else if(type[0]=='l') {
186 l->type = gfx_lineTo;
188 return PY_ERROR("need 2 values for line");
189 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
190 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
191 } else if(type[0]=='s') {
192 l->type = gfx_splineTo;
194 return PY_ERROR("need 4 values for spline");
195 l->x = PyFloat_AsDouble(PyTuple_GetItem(p, 1));
196 l->y = PyFloat_AsDouble(PyTuple_GetItem(p, 2));
197 l->sx = PyFloat_AsDouble(PyTuple_GetItem(p, 3));
198 l->sy = PyFloat_AsDouble(PyTuple_GetItem(p, 4));
200 return PY_ERROR("Unknown line code '%s'", type);
206 PyDoc_STRVAR(output_fillbitmap_doc, \
208 "fill a polygon with a bitmap pattern\n"
210 static PyObject* output_fillbitmap(PyObject* _self, PyObject* args, PyObject* kwargs)
212 OutputObject* self = (OutputObject*)_self;
215 static char *kwlist[] = {"line", "bitmap", NULL};
217 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", kwlist, &PyList_Type, &_line, &_bitmap))
220 gfximage_t*image = toImage(_bitmap);
222 return PY_ERROR("invalid image");
224 gfxline_t*line = toLine(_line);
230 memset(&m, 0, sizeof(gfxmatrix_t));
233 self->output_device->fillbitmap(self->output_device, line, image, &m, 0);
238 PyDoc_STRVAR(output_fill_doc, \
240 "fill a polygon with a color\n"
242 static PyObject* output_fill(PyObject* _self, PyObject* args, PyObject* kwargs)
244 OutputObject* self = (OutputObject*)_self;
247 static char *kwlist[] = {"line", "color", NULL};
251 int a=255,r=0,g=0,b=0;
252 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", kwlist, &PyList_Type, &_line, &color))
255 if(!PyArg_ParseTuple(color, "iiii:color", &a, &r, &g, &b)) {
260 c.r = r; c.g = g; c.b = b; c.a = a;
262 gfxline_t*line = toLine(_line);
268 memset(&m, 0, sizeof(gfxmatrix_t));
271 self->output_device->fill(self->output_device, line, &c);
276 PyDoc_STRVAR(output_stroke_doc, \
278 "stroke a polygon with a color\n"
280 static PyObject* output_stroke(PyObject* _self, PyObject* args, PyObject* kwargs)
282 OutputObject* self = (OutputObject*)_self;
285 static char *kwlist[] = {"line", "width", "color", NULL};
289 int a=255,r=0,g=0,b=0;
291 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!fO", kwlist, &PyList_Type, &_line, &width, &color))
294 if(!PyArg_ParseTuple(color, "iiii:color", &a, &r, &g, &b)) {
299 c.r = r; c.g = g; c.b = b; c.a = a;
301 gfxline_t*line = toLine(_line);
305 self->output_device->stroke(self->output_device, line, width, &c,
306 /*TODO*/ gfx_capRound, gfx_joinRound, 0.0);
311 PyDoc_STRVAR(output_endpage_doc, \
313 "Ends a page in the output device. This function should be called\n"
314 "once for every startpage()\n"
316 static PyObject* output_endpage(PyObject* _self, PyObject* args, PyObject* kwargs)
318 OutputObject* self = (OutputObject*)_self;
319 if (!PyArg_ParseTuple(args, ""))
321 self->output_device->endpage(self->output_device);
324 PyDoc_STRVAR(output_setparameter_doc, \
325 "setparameter(key, value)\n\n"
326 "Set a output-device dependent parameter"
328 static PyObject* output_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
330 OutputObject* self = (OutputObject*)_self;
331 static char *kwlist[] = {"key", "value", NULL};
333 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
335 self->output_device->setparameter(self->output_device, key, value);
338 PyDoc_STRVAR(f_createSWF_doc, \
340 "Creates a device which renders documents to SWF (Flash) files.\n"
341 "Depending on the way the document parser behaves (see the poly2bitmap\n"
342 "and bitmap parameters), the resulting SWF might use vector operations\n"
343 "and Flash Texts to display the document, or just a single bitmap.\n"
345 static PyObject* f_createSWF(PyObject* parent, PyObject* args, PyObject* kwargs)
347 static char *kwlist[] = {NULL};
348 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
350 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
352 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
353 gfxdevice_swf_init(self->output_device);
354 return (PyObject*)self;
357 PyDoc_STRVAR(f_createOCR_doc, \
359 "Creates a device which processes documents using OCR (optical\n"
360 "character recognition).\n"
361 "This is handy for e.g. extracting fulltext from PDF documents\n"
362 "which have broken fonts, and where hence the \"PlainText\"\n"
363 "device doesn't work.\n"
365 static PyObject* f_createOCR(PyObject* parent, PyObject* args, PyObject* kwargs)
367 static char *kwlist[] = {NULL};
368 if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
370 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
372 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
373 gfxdevice_ocr_init(self->output_device);
374 return (PyObject*)self;
378 PyDoc_STRVAR(f_createImageList_doc, \
380 "Creates a device which renders documents to bitmaps.\n"
381 "Each page that is rendered will create new bitmap.\n"
382 "Using save(), you can save the images to a number\n"
385 static PyObject* f_createImageList(PyObject* parent, PyObject* args, PyObject* kwargs)
387 static char *kwlist[] = {NULL};
388 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
390 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
392 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
393 gfxdevice_render_init(self->output_device);
394 return (PyObject*)self;
397 PyDoc_STRVAR(f_createPlainText_doc, \
399 "Creates a device which can be used to extract text from documents,\n"
400 "by passing it as parameter to page.render().\n"
401 "The extracted text can be saved by plaintext.save(filename).\n"
403 static PyObject* f_createPlainText(PyObject* parent, PyObject* args, PyObject* kwargs)
405 static char *kwlist[] = {NULL};
406 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
408 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
410 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
411 gfxdevice_text_init(self->output_device);
412 return (PyObject*)self;
416 PyDoc_STRVAR(f_createOpenGL_doc, \
418 "Creates a device which renders everything to OpenGL.\n"
419 "Can be used for desktop display and debugging.\n"
420 "This device is not available on all systems.\n"
422 static PyObject* f_createOpenGL(PyObject* parent, PyObject* args, PyObject* kwargs)
424 static char *kwlist[] = {NULL};
425 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
427 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
429 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
430 gfxdevice_opengl_init(self->output_device);
431 return (PyObject*)self;
435 static char callback_python(char*function, gfxdevice_t*dev, const char*format, ...)
437 OutputObject*self = (OutputObject*)dev->internal;
439 if(!PyObject_HasAttrString(self->pyobj, function))
443 va_start(ap, format);
445 PyObject*tuple = PyTuple_New(strlen(format));
448 char p = format[pos];
450 char*s = va_arg(ap, char*);
451 PyTuple_SetItem(tuple, pos, PyString_FromString(s));
453 int i = va_arg(ap, int);
454 PyTuple_SetItem(tuple, pos, PyInt_FromLong(i));
456 int i = va_arg(ap, double);
457 PyTuple_SetItem(tuple, pos, PyFloat_FromDouble(i));
459 void* ptr = va_arg(ap, void*);
460 gfxcolor_t*col = (gfxcolor_t*)ptr;
461 PyObject*colobj = PyTuple_New(4);
462 PyTuple_SetItem(colobj, 0, PyInt_FromLong(col->a));
463 PyTuple_SetItem(colobj, 1, PyInt_FromLong(col->r));
464 PyTuple_SetItem(colobj, 2, PyInt_FromLong(col->g));
465 PyTuple_SetItem(colobj, 3, PyInt_FromLong(col->b));
466 PyTuple_SetItem(tuple, pos, colobj);
468 void* ptr = va_arg(ap, void*);
469 gfxline_t*line = (gfxline_t*)ptr;
473 while(l) {l=l->next;len++;}
474 PyObject*list = PyList_New(len);
478 if(l->type == gfx_moveTo) {
479 point = PyTuple_New(3);
480 PyTuple_SetItem(point, 0, PyString_FromString("m"));
481 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
482 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
483 } else if(l->type == gfx_lineTo) {
484 point = PyTuple_New(3);
485 PyTuple_SetItem(point, 0, PyString_FromString("l"));
486 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
487 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
488 } else if(l->type == gfx_splineTo) {
489 point = PyTuple_New(5);
490 PyTuple_SetItem(point, 0, PyString_FromString("s"));
491 PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
492 PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
493 PyTuple_SetItem(point, 3, PyFloat_FromDouble(l->sx));
494 PyTuple_SetItem(point, 4, PyFloat_FromDouble(l->sy));
498 PyList_SetItem(list, i, point);
502 PyTuple_SetItem(tuple, pos, list);
504 PyTuple_SetItem(tuple, pos, PY_NONE);
509 PyObject*f = PyObject_GetAttrString(self->pyobj, function);
513 PyObject* result = PyObject_CallObject(f, tuple);
525 static int my_setparameter(gfxdevice_t*dev, const char*key, const char*value)
527 callback_python("setparameter", dev, "ss", key, value);
530 static void my_startpage(gfxdevice_t*dev, int width, int height)
532 callback_python("startpage", dev, "ii", width, height);
534 static void my_startclip(gfxdevice_t*dev, gfxline_t*line)
536 callback_python("startclip", dev, "l", line);
538 static void my_endclip(gfxdevice_t*dev)
540 callback_python("endclip", dev, "");
542 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)
546 if(cap_style == gfx_capButt)
548 else if(cap_style == gfx_capRound)
550 else if(cap_style == gfx_capSquare)
552 if(joint_style == gfx_joinMiter)
554 else if(joint_style == gfx_joinRound)
556 else if(joint_style == gfx_joinBevel)
558 callback_python("stroke", dev, "ldcssi", line, width, color, cap, joint, miterLimit);
560 static void my_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
562 callback_python("fill", dev, "lc", line, color);
564 static void my_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*imgcoord2devcoord, gfxcxform_t*cxform)
566 callback_python("fillbitmap", dev, "lImx", line, img, imgcoord2devcoord, cxform);
568 static void my_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
570 callback_python("fillgradient", dev, "lgsm", line, gradient, type, matrix);
572 static void my_addfont(gfxdevice_t*dev, gfxfont_t*font)
574 callback_python("addfont", dev, "f", font);
576 static void my_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
578 if(!callback_python("drawchar", dev, "ficm", font, glyphnr, color, matrix))
582 gfxglyph_t*glyph = &font->glyphs[glyphnr];
583 gfxline_t*line2 = gfxline_clone(glyph->line);
584 gfxline_transform(line2, matrix);
585 my_fill(dev, line2, color);
590 static void my_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
592 callback_python("drawlink", dev, "ls", line, action);
594 static void my_endpage(gfxdevice_t*dev)
596 callback_python("drawlink", dev, "");
598 static gfxresult_t* my_finish(gfxdevice_t*dev)
600 callback_python("finish", dev, "");
605 PyDoc_STRVAR(f_createPassThrough_doc, \
606 "PassThrough(device)\n\n"
607 "Creates a PassThrough device, which can be used as parameter in calls\n"
608 "to page.render().\n"
609 "device needs to be a class implementing at least the following functions:\n\n"
610 "setparameter(key,value)\n"
611 "startclip(outline)\n"
613 "stroke(outline, width, color, capstyle, jointstyle, miterLimit)\n"
614 "fill(outline, color)\n"
615 "fillbitmap(outline, image, matrix, colortransform)\n"
616 "fillgradient(outline, gradient, gradienttype, matrix)\n"
618 "drawchar(font, glyph, color, matrix)\n"
619 "drawlink(outline, url)\n"
620 "If any of these functions are not defined, a error message will be printed,\n"
621 "however the rendering process will *not* be aborted.\n"
623 static PyObject* f_createPassThrough(PyObject* parent, PyObject* args, PyObject* kwargs)
625 static char *kwlist[] = {"device", NULL};
627 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &obj))
629 OutputObject*self = PyObject_New(OutputObject, &OutputClass);
633 self->output_device = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
634 memset(self->output_device, 0, sizeof(gfxdevice_t));
635 self->output_device->name = strdup("passthrough");
637 self->output_device->setparameter = my_setparameter;
638 self->output_device->startpage = my_startpage;
639 self->output_device->startclip = my_startclip;
640 self->output_device->addfont = my_addfont;
641 self->output_device->endclip = my_endclip;
642 self->output_device->stroke = my_stroke;
643 self->output_device->fill = my_fill;
644 self->output_device->fillbitmap = my_fillbitmap;
645 self->output_device->fillgradient = my_fillgradient;
646 self->output_device->drawchar = my_drawchar;
647 self->output_device->drawlink = my_drawlink;
648 self->output_device->endpage = my_endpage;
649 self->output_device->finish = my_finish;
650 self->output_device->internal = self;
652 return (PyObject*)self;
655 static PyMethodDef output_methods[] =
657 /* Output functions */
658 {"save", (PyCFunction)output_save, METH_KEYWORDS, output_save_doc},
659 {"startpage", (PyCFunction)output_startpage, METH_KEYWORDS, output_startpage_doc},
660 {"fill", (PyCFunction)output_fill, METH_KEYWORDS, output_fill_doc},
661 {"fillbitmap", (PyCFunction)output_fillbitmap, METH_KEYWORDS, output_fillbitmap_doc},
662 {"stroke", (PyCFunction)output_stroke, METH_KEYWORDS, output_stroke_doc},
663 {"endpage", (PyCFunction)output_endpage, METH_KEYWORDS, output_endpage_doc},
664 {"setparameter", (PyCFunction)output_setparameter, METH_KEYWORDS, output_setparameter_doc},
668 static void output_dealloc(PyObject* _self) {
669 OutputObject* self = (OutputObject*)_self;
671 if(self->output_device) {
672 gfxresult_t*result = self->output_device->finish(self->output_device);
674 result->destroy(result);result=0;
676 self->output_device = 0;
681 static PyObject* output_getattr(PyObject * _self, char* a)
683 OutputObject*self = (OutputObject*)_self;
685 /* if(!strcmp(a, "x1")) {
686 return PyInt_FromLong(self->output_device->x1);
687 } else if(!strcmp(a, "y1")) {
688 return PyInt_FromLong(self->output_device->y1);
689 } else if(!strcmp(a, "x2")) {
690 return PyInt_FromLong(self->output_device->x2);
691 } else if(!strcmp(a, "y2")) {
692 return PyInt_FromLong(self->output_device->y2);
695 return Py_FindMethod(output_methods, _self, a);
697 static int output_setattr(PyObject * _self, char* a, PyObject * o)
699 OutputObject*self = (OutputObject*)_self;
700 if(!PyString_Check(o))
702 char*value = PyString_AsString(o);
703 self->output_device->setparameter(self->output_device, a, value);
706 static int output_print(PyObject * _self, FILE *fi, int flags)
708 OutputObject*self = (OutputObject*)_self;
709 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
713 //---------------------------------------------------------------------
714 staticforward PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs);
715 staticforward PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs);
717 PyDoc_STRVAR(page_render_doc, \
718 "render(output, move=(0,0), clip=None)\n\n"
719 "Renders a page to the rendering backend specified by the output\n"
720 "parameter. Rendering consists of calling a number of functions on the\n"
721 "output device, see the description of the \"PassThrough\" device.\n"
722 "The page may be shifted to a given position using the move parameter,\n"
723 "and may also be clipped to a specific size using the clip parameter.\n"
724 "The clipping operation is applied after the move operation.\n"
726 static PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs)
728 PageObject* self = (PageObject*)_self;
730 static char *kwlist[] = {"dev", "move", "clip", NULL};
731 OutputObject*output = 0;
734 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &OutputClass, &output,
740 int cx1=0,cy1=0,cx2=0,cy2=0;
743 if (!PyArg_ParseTuple(move, "ii", &x,&y))
747 if (!PyArg_ParseTuple(clip, "iiii", &cx1,&cy1,&cx2,&cy2))
751 if(x|y|cx1|cx2|cy1|cy2)
752 self->page->rendersection(self->page, output->output_device,x,y,cx1,cy1,cx2,cy2);
754 self->page->render(self->page, output->output_device);
758 PyDoc_STRVAR(page_asImage_doc, \
759 "asImage(width, height)\n\n"
760 "Creates a bitmap from a page. The bitmap will be returned as a string\n"
761 "containing RGB triplets. The bitmap will be rescaled to the specified width and\n"
762 "height. The aspect ratio of width and height doesn't need to be the same\n"
765 static PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs)
767 PageObject* self = (PageObject*)_self;
769 static char *kwlist[] = {"width", "height", NULL};
770 int width=0,height=0;
771 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &width, &height))
774 if(!width || !height) {
775 return PY_ERROR("invalid dimensions: %dx%d", width,height);
778 gfxdevice_t dev1,dev2;
779 gfxdevice_render_init(&dev1);
780 dev1.setparameter(&dev1, "antialise", "2");
781 gfxdevice_rescale_init(&dev2, &dev1, width, height, 0);
782 dev2.startpage(&dev2, self->page->width, self->page->height);
783 self->page->render(self->page, &dev2);
785 gfxresult_t*result = dev2.finish(&dev2);
786 gfximage_t*img = (gfximage_t*)result->get(result,"page0");
787 int l = img->width*img->height;
788 unsigned char*data = (unsigned char*)malloc(img->width*img->height*3);
790 for(t=0,s=0;t<l;s+=3,t++) {
791 data[s+0] = img->data[t].r;
792 data[s+1] = img->data[t].g;
793 data[s+2] = img->data[t].b;
795 result->destroy(result);
796 return PyString_FromStringAndSize((char*)data,img->width*img->height*3);
799 static PyMethodDef page_methods[] =
802 {"render", (PyCFunction)page_render, METH_KEYWORDS, page_render_doc},
803 {"asImage", (PyCFunction)page_asImage, METH_KEYWORDS, page_asImage_doc},
806 static void page_dealloc(PyObject* _self) {
807 PageObject* self = (PageObject*)_self;
809 self->page->destroy(self->page);
813 Py_DECREF(self->parent);
820 static PyObject* page_getattr(PyObject * _self, char* a)
822 PageObject*self = (PageObject*)_self;
824 if(!strcmp(a, "size")) {
825 return Py_BuildValue("(ii)", self->page->width, self->page->height);
826 } if(!strcmp(a, "doc")) {
827 Py_INCREF(self->parent);
829 } if(!strcmp(a, "nr")) {
830 return PyInt_FromLong(self->nr);
831 } else if(!strcmp(a, "width")) {
832 return PyInt_FromLong(self->page->width);
833 } else if(!strcmp(a, "height")) {
834 return PyInt_FromLong(self->page->height);
836 return Py_FindMethod(page_methods, _self, a);
839 static int page_setattr(PyObject * self, char* a, PyObject * o) {
842 static int page_print(PyObject * _self, FILE *fi, int flags)
844 PageObject*self = (PageObject*)_self;
845 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
849 //---------------------------------------------------------------------
851 PyDoc_STRVAR(doc_getPage_doc,
853 "Get one page from a document file. The nr parameter specifies\n"
854 "which page to retrieve. Counting starts at 1, so the first page\n"
855 "can be retrieved by\n"
856 " page = doc.getPage(1)\n"
858 "You can find out how many pages a document contains by querying\n"
859 "its pages field (doc.pages)\n"
861 static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
863 DocObject* self = (DocObject*)_self;
865 static char *kwlist[] = {"nr", NULL};
867 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pagenr))
870 PageObject*page = PyObject_New(PageObject, &PageClass);
871 page->page = self->doc->getpage(self->doc, pagenr);
873 page->parent = _self;
874 Py_INCREF(page->parent);
877 return PY_ERROR("Couldn't extract page %d", pagenr);
879 return (PyObject*)page;
882 PyDoc_STRVAR(doc_getInfo_doc,
884 "Retrieve some information about a document. For PDF files, key\n"
885 "can have the following values:\n\n"
886 "\"title\", \"subject\", \"keywords\", \"author\", \"creator\", \"producer\",\n"
887 "\"creationdate\", \"moddate\", \"linearized\", \"tagged\", \"encrypted\",\n"
888 "\"oktoprint\", \"oktocopy\", \"oktochange\", \"oktoaddnotes\", \"version\".\n\n"
889 "If the \"oktocopy\" digital rights management flag is set to \"no\", then the\n"
890 "pdf parser won't allow you to access the PDF file. Trying to extract pages\n"
891 "from it will raise an exception.\n"
893 static PyObject* doc_getInfo(PyObject* _self, PyObject* args, PyObject* kwargs)
895 DocObject* self = (DocObject*)_self;
897 static char *kwlist[] = {"key", NULL};
899 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &key))
902 char*s = self->doc->getinfo(self->doc, key);
903 return PyString_FromString(s);
906 PyDoc_STRVAR(doc_setparameter_doc,
907 "setparameter(key, value)\n\n"
908 "Pass a parameter or setting to the document parser. Unlike\n"
909 "the module level setparameter() function, the parameters set\n"
910 "using setparameter will only be valid for the object itself\n"
911 "during its lifetime.\n"
913 static PyObject* doc_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
915 DocObject* self = (DocObject*)_self;
917 static char *kwlist[] = {"key", "value", NULL};
918 char*key = 0, *value=0;
919 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key,&value))
922 self->doc->setparameter(self->doc, key, value);
926 PyDoc_STRVAR(f_open_doc,
927 "open(type, filename) -> object\n\n"
928 "Open a PDF, SWF or image file. The type argument should be \"pdf\",\n"
929 "\"swf\" or \"image\" accordingly. It returns a doc object which can be\n"
930 "used to process the file contents.\n"
932 " doc = open(\"pdf\", \"document.pdf\")\n"
933 " doc = open(\"swf\", \"flashfile.swf\")\n"
934 " doc = open(\"image\", \"image.png\")\n"
935 "If the file could not be loaded, or is a encrypted PDF file without\n"
936 "a proper password specified, an exception is being raised.\n"
937 "If the filename argument contains a '|' char, everything behind\n"
938 "the '|' is treated as password used for opening the file.\n"
940 " doc = open(\"pdf\", \"document.pdf|mysecretpassword\")\n"
942 "Notice that for image files, the only supported file formats right now\n"
943 "are jpeg and png.\n"
945 static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
947 static char *kwlist[] = {"type", "filename", NULL};
950 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
951 static char *kwlist2[] = {"filename", NULL};
954 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist2, &filename))
958 DocObject*self = PyObject_New(DocObject, &DocClass);
960 if(!type) { //autodetect
961 type = "pdf"; //default
962 int l = strlen(filename);
964 if(filename[l-4]=='.') {
965 if(strchr("pP", filename[l-3]) && strchr("dD", filename[l-2]) && strchr("fF", filename[l-1]))
967 if(strchr("jJ", filename[l-3]) && strchr("pP", filename[l-2]) && strchr("gG", filename[l-1]))
969 if(strchr("pP", filename[l-3]) && strchr("nN", filename[l-2]) && strchr("gG", filename[l-1]))
971 if(strchr("sS", filename[l-3]) && strchr("wW", filename[l-2]) && strchr("fF", filename[l-1]))
973 } else if(filename[l-5]=='.') {
979 if(!strcmp(type,"pdf"))
980 self->doc = pdfdriver->open(pdfdriver,filename);
981 else if(!strcmp(type, "image") || !strcmp(type, "img"))
982 self->doc = imagedriver->open(imagedriver, filename);
983 else if(!strcmp(type, "swf") || !strcmp(type, "SWF"))
984 self->doc = swfdriver->open(imagedriver, filename);
986 return PY_ERROR("Unknown type %s", type);
990 return PY_ERROR("Couldn't open %s", filename);
992 self->filename = strdup(filename);
993 return (PyObject*)self;
996 static PyMethodDef doc_methods[] =
999 {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, doc_getPage_doc},
1000 {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, doc_getInfo_doc},
1001 {"setparameter", (PyCFunction)doc_setparameter, METH_KEYWORDS, doc_setparameter_doc},
1005 static void doc_dealloc(PyObject* _self) {
1006 DocObject* self = (DocObject*)_self;
1008 self->doc->destroy(self->doc);
1011 if(self->filename) {
1012 free(self->filename);self->filename=0;
1016 static PyObject* doc_getattr(PyObject * _self, char* a)
1018 DocObject*self = (DocObject*)_self;
1019 if(!strcmp(a, "pages")) {
1020 return PyInt_FromLong(self->doc->num_pages);
1022 if(!strcmp(a, "filename")) {
1023 return PyString_FromString(self->filename);
1025 return Py_FindMethod(doc_methods, _self, a);
1027 static int doc_setattr(PyObject * self, char* a, PyObject * o) {
1030 static int doc_print(PyObject * _self, FILE *fi, int flags)
1032 DocObject*self = (DocObject*)_self;
1033 fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
1037 //---------------------------------------------------------------------
1039 PyDoc_STRVAR(output_doc,
1040 "An Output object can be used as parameter to the render()\n"
1041 "call of a page. It's not possible to create this type of\n"
1042 "object directly (i.e., from a class), however you can\n"
1043 "use a PassThrough() device to pass things over to Python.\n"
1044 "Examples for classes implementing the Output class are: \n"
1045 "ImageList, SWF, PlainText and PassThrough.\n"
1047 static PyTypeObject OutputClass =
1049 PyObject_HEAD_INIT(NULL)
1051 tp_name: "gfx.Output",
1052 tp_basicsize: sizeof(OutputObject),
1054 tp_dealloc: output_dealloc,
1055 tp_print: output_print,
1056 tp_getattr: output_getattr,
1057 tp_setattr: output_setattr,
1059 tp_methods: output_methods
1061 PyDoc_STRVAR(page_doc,
1062 "A Page object contains a single page of a document.\n"
1063 "page.width and page.height (or page.size) contain the\n"
1064 "page dimensions. page.nr is the number of the page, and\n"
1065 "page.doc is the parent document.\n"
1067 static PyTypeObject PageClass =
1069 PyObject_HEAD_INIT(NULL)
1071 tp_name: "gfx.Page",
1072 tp_basicsize: sizeof(PageObject),
1074 tp_dealloc: page_dealloc,
1075 tp_print: page_print,
1076 tp_getattr: page_getattr,
1077 tp_setattr: page_setattr,
1079 tp_methods: page_methods
1081 PyDoc_STRVAR(doc_doc,
1082 "A Doc object is used for storing a document (like a PDF).\n"
1083 "doc.pages contains the number of pages in the document,\n"
1084 "and doc.filename the name of the file the document was\n"
1085 "created (loaded) from. If the document was created from\n"
1086 "an image file, the number of pages is always 1\n"
1088 static PyTypeObject DocClass =
1090 PyObject_HEAD_INIT(NULL)
1093 tp_basicsize: sizeof(DocObject),
1095 tp_dealloc: doc_dealloc,
1096 tp_print: doc_print,
1097 tp_getattr: doc_getattr,
1098 tp_setattr: doc_setattr,
1100 tp_methods: doc_methods,
1103 //=====================================================================
1105 PyDoc_STRVAR(f_setparameter_doc, \
1106 "setparameter(key,value)\n\n"
1107 "Set a parameter in the gfx module (which might affect the PDF\n"
1108 "parser or any of the rendering backends). This is a parameter\n"
1109 "which would usually be passed with the \"-s\" option to pdf2swf.\n"
1110 "For a list of all parameters, see the output of\n"
1111 " pdf2swf -s help\n"
1113 " pdf2swf somefile.pdf -s help\n"
1116 static PyObject* f_setparameter(PyObject* self, PyObject* args, PyObject* kwargs)
1118 static char *kwlist[] = {"key", "value", NULL};
1119 char*key=0,*value=0;
1120 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
1122 pdfdriver->setparameter(pdfdriver,key,value);
1126 PyDoc_STRVAR(f_verbose_doc, \
1127 "verbose(level)\n\n"
1128 "Set the logging verbosity of the gfx module. Log levels are:\n"
1129 "level=-1 Log nothing\n"
1130 "level=0 (fatal) Log only fatal errors\n"
1131 "level=1 (error) Log only fatal errors and errors\n"
1132 "level=2 (warn) Log all errors and warnings\n"
1133 "level=3 (notice) Log also some rudimentary data about the parsing/conversion\n"
1134 "level=4 (verbose) Log some additional parsing information\n"
1135 "level=5 (debug) Log debug statements\n"
1136 "level=6 (trace) Log extended debug statements\n"
1137 "All logging messages are written to stdout.\n"
1139 static PyObject* f_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
1141 static char *kwlist[] = {"val", NULL};
1143 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &val))
1145 setConsoleLogging(val);
1149 PyDoc_STRVAR(f_addfont_doc, \
1150 "addfont(filename)\n\n"
1151 "Passes an additional font file to the PDF parser. If a PDF contains\n"
1152 "external fonts (i.e. fonts which are not contained in the PDF itself)\n"
1153 "then the files added by addfont() will be searched.\n"
1156 static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
1158 static char *kwlist[] = {"filename", NULL};
1160 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
1162 pdfdriver->setparameter(pdfdriver,"font", filename);
1166 PyDoc_STRVAR(f_addfontdir_doc, \
1167 "addfontdir(dirname)\n\n"
1168 "Passes a complete directory containing fonts to the PDF parser. Any\n"
1169 "font file within this directory might be used to resolve external fonts\n"
1172 static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
1174 static char *kwlist[] = {"filename", NULL};
1176 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
1178 pdfdriver->setparameter(pdfdriver,"fontdir", filename);
1182 static PyMethodDef pdf2swf_methods[] =
1185 {"open", (PyCFunction)f_open, METH_KEYWORDS, f_open_doc},
1186 {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, f_addfont_doc},
1187 {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, f_addfontdir_doc},
1188 {"setparameter", (PyCFunction)f_setparameter, METH_KEYWORDS, f_setparameter_doc},
1189 {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, f_verbose_doc},
1192 {"SWF", (PyCFunction)f_createSWF, METH_KEYWORDS, f_createSWF_doc},
1193 {"OCR", (PyCFunction)f_createOCR, METH_KEYWORDS, f_createOCR_doc},
1194 {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, f_createImageList_doc},
1195 {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, f_createPlainText_doc},
1196 {"PassThrough", (PyCFunction)f_createPassThrough, METH_KEYWORDS, f_createPassThrough_doc},
1198 {"OpenGL", (PyCFunction)f_createOpenGL, METH_KEYWORDS, f_createOpenGL_doc},
1205 PyDoc_STRVAR(gfx_doc, \
1206 "This module contains a PDF parser (based on xpdf) and a number of\n"
1207 "rendering backends. In particular, it can extract text from PDF pages,\n"
1208 "create bitmaps from them, or convert PDF files to SWF.\n"
1209 "The latter functionality is similar to what is offered by swftools'\n"
1210 "(http://www.swftools.org) pdf2swf utility, however more powerful-\n"
1211 "You can also create individual SWF files from single pages of the PDF\n"
1212 "or mix pages from different PDF files.\n"
1217 initLog(0,0,0,0,0,2);
1218 OutputClass.ob_type = &PyType_Type;
1219 PageClass.ob_type = &PyType_Type;
1220 DocClass.ob_type = &PyType_Type;
1222 pdfdriver = gfxsource_pdf_create();
1223 swfdriver = gfxsource_swf_create();
1224 imagedriver = gfxsource_image_create();
1226 PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
1227 PyObject*module_dict = PyModule_GetDict(module);
1229 PyDict_SetItemString(module_dict, "Doc", (PyObject*)&DocClass);
1230 PyDict_SetItemString(module_dict, "Page", (PyObject*)&PageClass);
1231 PyDict_SetItemString(module_dict, "Output", (PyObject*)&OutputClass);