added OCR device
[swftools.git] / lib / python / gfx.c
1 /* gfx.c
2
3    Python wrapper for gfx convert
4
5    Part of the swftools package.
6
7    Copyright (c) 2003 Matthias Kramm <kramm@quiss.org>
8  
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.
13
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.
18
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 */
22
23 #include <Python.h>
24 #include <stdarg.h>
25 #undef HAVE_STAT
26 #include "../devices/swf.h"
27 #include "../devices/render.h"
28 #include "../devices/ocr.h"
29 #include "../devices/rescale.h"
30 #include "../devices/text.h"
31 #include "../pdf/pdf.h"
32 #include "../readers/swf.h"
33 #include "../readers/image.h"
34 #include "../log.h"
35 #include "../utf8.h"
36
37 static gfxsource_t*pdfdriver = 0;
38 static gfxsource_t*swfdriver = 0;
39 static gfxsource_t*imagedriver = 0;
40
41 staticforward PyTypeObject OutputClass;
42 staticforward PyTypeObject PageClass;
43 staticforward PyTypeObject DocClass;
44
45 typedef struct {
46     PyObject_HEAD
47     gfxdevice_t*output_device;
48     PyObject*pyobj; //only for passthrough
49 } OutputObject;
50
51 typedef struct {
52     PyObject_HEAD
53     PyObject*parent;
54     gfxpage_t*page;
55     int nr;
56 } PageObject;
57
58 typedef struct {
59     PyObject_HEAD
60     gfxdocument_t*doc;
61     char*filename;
62 } DocObject;
63
64 static char* strf(char*format, ...)
65 {
66     char buf[1024];
67     int l;
68     va_list arglist;
69     va_start(arglist, format);
70     vsprintf(buf, format, arglist);
71     va_end(arglist);
72     return strdup(buf);
73 }
74 #define PY_ERROR(s,args...) (PyErr_SetString(PyExc_Exception, strf(s, ## args)),NULL)
75 #define PY_NONE Py_BuildValue("s", 0)
76
77 //---------------------------------------------------------------------
78 PyDoc_STRVAR(output_save_doc, \
79 "save(filename)\n\n"
80 "Saves the contents of an output device to a file\n"
81 "Depending on what the output device is, the contents\n"
82 "of the file may be plain text, an image, an SWF file,\n"
83 "etc.\n"
84 "For the ImageList device, several files (named\n"
85 "filename.1.png, filename.2.png etc.) might be created)\n"
86 );
87 static PyObject* output_save(PyObject* _self, PyObject* args, PyObject* kwargs)
88 {
89     OutputObject* self = (OutputObject*)_self;
90     char*filename = 0;
91     static char *kwlist[] = {"filename", NULL};
92     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
93         return NULL;
94
95     gfxresult_t*result = self->output_device->finish(self->output_device);
96     self->output_device = 0;
97     if(result->save(result, filename) < 0) {
98         return PY_ERROR("Couldn't write to %s", filename);
99     }
100     result->destroy(result);
101     return PY_NONE;
102 }
103
104 PyDoc_STRVAR(output_startpage_doc, \
105 "startpage(width, height)\n\n"
106 "Starts a new page/frame in the output device.\n"
107 "The usual way to render documents is to start a new page in the\n"
108 "device for each page in the document:\n"
109 "\n"
110 "for pagenr in range(1,doc.pages+1):\n"
111 "    page = doc.getPage(pagenr)\n"
112 "    output.startpage(page.width, page.height)\n"
113 "    page.render(output)\n"
114 "    output.endpage()\n"
115 "\n"
116 "It is, however, also possible to render more than one document page\n"
117 "to a single output page. E.g. for side-by-side or book views.\n"
118 );
119 static PyObject* output_startpage(PyObject* _self, PyObject* args, PyObject* kwargs)
120 {
121     OutputObject* self = (OutputObject*)_self;
122     int width=0, height=0;
123     if (!PyArg_ParseTuple(args, "ii", &width, &height))
124         return NULL;
125     self->output_device->startpage(self->output_device, width, height);
126     return PY_NONE;
127 }
128 PyDoc_STRVAR(output_endpage_doc, \
129 "endpage()\n\n"
130 "Ends a page in the output device. This function should be called\n"
131 "once for every startpage()\n"
132 );
133 static PyObject* output_endpage(PyObject* _self, PyObject* args, PyObject* kwargs)
134 {
135     OutputObject* self = (OutputObject*)_self;
136     if (!PyArg_ParseTuple(args, ""))
137         return NULL;
138     self->output_device->endpage(self->output_device);
139     return PY_NONE;
140 }
141 PyDoc_STRVAR(output_setparameter_doc, \
142 "setparameter(key, value)\n\n"
143 "Set a output-device dependent parameter"
144 );
145 static PyObject* output_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
146 {
147     OutputObject* self = (OutputObject*)_self;
148     static char *kwlist[] = {"key", "value", NULL};
149     char*key=0,*value=0;
150     if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
151         return NULL;
152     self->output_device->setparameter(self->output_device, key, value);
153     return PY_NONE;
154 }
155 PyDoc_STRVAR(f_createSWF_doc, \
156 "SWF()\n\n"
157 "Creates a device which renders documents to SWF (Flash) files.\n"
158 "Depending on the way the document parser behaves (see the poly2bitmap\n"
159 "and bitmap parameters), the resulting SWF might use vector operations\n"
160 "and Flash Texts to display the document, or just a single bitmap.\n"
161 );
162 static PyObject* f_createSWF(PyObject* parent, PyObject* args, PyObject* kwargs)
163 {
164     static char *kwlist[] = {NULL};
165     if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
166         return NULL;
167     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
168     
169     self->output_device = malloc(sizeof(gfxdevice_t));
170     gfxdevice_swf_init(self->output_device);
171     return (PyObject*)self;
172 }
173
174 PyDoc_STRVAR(f_createOCR_doc, \
175 "OCR()\n\n"
176 "Creates a device which processes documents using OCR (optical\n"
177 "character recognition).\n"
178 "This is handy for e.g. extracting fulltext from PDF documents\n"
179 "which have broken fonts, and where hence the \"PlainText\"\n"
180 "device doesn't work.\n"
181 );
182 static PyObject* f_createOCR(PyObject* parent, PyObject* args, PyObject* kwargs)
183 {
184     static char *kwlist[] = {NULL};
185     if (args && !PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
186         return NULL;
187     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
188     
189     self->output_device = malloc(sizeof(gfxdevice_t));
190     gfxdevice_ocr_init(self->output_device);
191     return (PyObject*)self;
192 }
193
194
195 PyDoc_STRVAR(f_createImageList_doc, \
196 "ImageList()\n\n"
197 "Creates a device which renders documents to bitmaps.\n"
198 "Each page that is rendered will create new bitmap.\n"
199 "Using save(), you can save the images to a number\n"
200 "of files\n"
201 );
202 static PyObject* f_createImageList(PyObject* parent, PyObject* args, PyObject* kwargs)
203 {
204     static char *kwlist[] = {NULL};
205     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
206         return NULL;
207     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
208     
209     self->output_device = malloc(sizeof(gfxdevice_t));
210     gfxdevice_render_init(self->output_device);
211     return (PyObject*)self;
212 }
213
214 PyDoc_STRVAR(f_createPlainText_doc, \
215 "PlainText()\n\n"
216 "Creates a device which can be used to extract text from documents,\n"
217 "by passing it as parameter to page.render().\n"
218 "The extracted text can be saved by plaintext.save(filename).\n"
219 );
220 static PyObject* f_createPlainText(PyObject* parent, PyObject* args, PyObject* kwargs)
221 {
222     static char *kwlist[] = {NULL};
223     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", kwlist))
224         return NULL;
225     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
226     
227     self->output_device = malloc(sizeof(gfxdevice_t));
228     gfxdevice_text_init(self->output_device);
229     return (PyObject*)self;
230 }
231
232 static PyObject*callback_python(char*function, gfxdevice_t*dev, const char*format, ...)
233 {
234     OutputObject*self = (OutputObject*)dev->internal;
235     
236     if(!PyObject_HasAttrString(self->pyobj, function))
237         return PY_NONE;
238
239     va_list ap;
240     va_start(ap, format);
241
242     PyObject*tuple = PyTuple_New(strlen(format));
243     int pos = 0;
244     while(format[pos]) {
245         char p = format[pos];
246         if(p=='s') {
247             char*s = va_arg(ap, char*);
248             PyTuple_SetItem(tuple, pos, PyString_FromString(s));
249         } else if(p=='i') {
250             int i = va_arg(ap, int);
251             PyTuple_SetItem(tuple, pos, PyInt_FromLong(i));
252         } else if(p=='c') {
253             void* ptr = va_arg(ap, void*);
254             gfxcolor_t*col = (gfxcolor_t*)ptr;
255             PyObject*colobj = PyTuple_New(4);
256             PyTuple_SetItem(colobj, 0, PyInt_FromLong(col->r));
257             PyTuple_SetItem(colobj, 1, PyInt_FromLong(col->g));
258             PyTuple_SetItem(colobj, 2, PyInt_FromLong(col->b));
259             PyTuple_SetItem(colobj, 3, PyInt_FromLong(col->a));
260             PyTuple_SetItem(tuple, pos, colobj);
261         } else if(p=='l') {
262             void* ptr = va_arg(ap, void*);
263             gfxline_t*line = (gfxline_t*)ptr;
264             gfxline_t*l;
265             int len = 0, i = 0;
266             l = line;
267             while(l) {l=l->next;len++;}
268             PyObject*list = PyList_New(len);
269             l = line;
270             while(l) {
271                 PyObject*point=0;
272                 if(l->type == gfx_moveTo) {
273                     point = PyTuple_New(3);
274                     PyTuple_SetItem(point, 0, PyString_FromString("m"));
275                     PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
276                     PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
277                 } else if(l->type == gfx_lineTo) {
278                     point = PyTuple_New(3);
279                     PyTuple_SetItem(point, 0, PyString_FromString("l"));
280                     PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
281                     PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
282                 } else if(l->type == gfx_splineTo) {
283                     point = PyTuple_New(5);
284                     PyTuple_SetItem(point, 0, PyString_FromString("s"));
285                     PyTuple_SetItem(point, 1, PyFloat_FromDouble(l->x));
286                     PyTuple_SetItem(point, 2, PyFloat_FromDouble(l->y));
287                     PyTuple_SetItem(point, 3, PyFloat_FromDouble(l->sx));
288                     PyTuple_SetItem(point, 4, PyFloat_FromDouble(l->sy));
289                 } else {
290                     point = PY_NONE;
291                 }
292                 PyList_SetItem(list, i, point);
293                 l = l->next;
294                 i++;
295             }
296             PyTuple_SetItem(tuple, pos, list);
297         } else {
298             PyTuple_SetItem(tuple, pos, PY_NONE);
299         }
300         pos++;
301     }
302     va_end(ap);
303     PyObject*f = PyObject_GetAttrString(self->pyobj, function);
304     if(!f)
305         return 0;
306     PyErr_Clear();
307     PyObject* result = PyObject_CallObject(f, tuple);
308
309     if(!result) { 
310         PyErr_Print();
311         PyErr_Clear();
312         return 0;
313     } else {
314         Py_DECREF(result);
315         return 0;
316     }
317 }
318     
319 static int my_setparameter(gfxdevice_t*dev, const char*key, const char*value)
320 {
321     callback_python("setparameter", dev, "ss", key, value);
322     return 1;
323 }
324 static void my_startpage(gfxdevice_t*dev, int width, int height)
325 {
326     callback_python("startpage", dev, "ii", width, height);
327 }
328 static void my_startclip(gfxdevice_t*dev, gfxline_t*line)
329 {
330     callback_python("startclip", dev, "l", line);
331 }
332 static void my_endclip(gfxdevice_t*dev)
333 {
334     callback_python("endclip", dev, "");
335 }
336 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)
337 {
338     char*cap = 0;
339     char*joint = 0;
340     if(cap_style == gfx_capButt)
341         cap = "butt";
342     else if(cap_style == gfx_capRound)
343         cap = "round";
344     else if(cap_style == gfx_capSquare)
345         cap = "square";
346     if(joint_style == gfx_joinMiter)
347         joint = "miter";
348     else if(joint_style == gfx_joinRound)
349         joint = "round";
350     else if(joint_style == gfx_joinBevel)
351         joint = "bevel";
352     callback_python("stroke", dev, "licssi", line, width, color, cap, joint, miterLimit);
353 }
354 static void my_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
355 {
356     callback_python("fill", dev, "lc", line, color);
357 }
358 static void my_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*imgcoord2devcoord, gfxcxform_t*cxform)
359 {
360     callback_python("fillbitmap", dev, "lImx", line, img, imgcoord2devcoord, cxform);
361 }
362 static void my_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
363 {
364     callback_python("fillgradient", dev, "lgsm", line, gradient, type, matrix);
365 }
366 static void my_addfont(gfxdevice_t*dev, gfxfont_t*font)
367 {
368     callback_python("addfont", dev, "f", font);
369 }
370 static void my_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix)
371 {
372     callback_python("drawchar", dev, "ficm", font, glyph, color, matrix);
373 }
374 static void my_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
375 {
376     callback_python("drawlink", dev, "ls", line, action);
377 }
378 static void my_endpage(gfxdevice_t*dev)
379 {
380     callback_python("drawlink", dev, "");
381 }
382 static gfxresult_t* my_finish(gfxdevice_t*dev)
383 {
384     callback_python("finish", dev, "");
385     return 0;
386 }
387
388
389 PyDoc_STRVAR(f_createPassThrough_doc, \
390 "PassThrough(device)\n\n"
391 "Creates a PassThrough device, which can be used as parameter in calls\n"
392 "to page.render().\n"
393 "device needs to be a class implementing at least the following functions:\n\n"
394 "setparameter(key,value)\n"
395 "startclip(outline)\n"
396 "endclip()\n"
397 "stroke(outline, width, color, capstyle, jointstyle, miterLimit)\n"
398 "fill(outline, color)\n"
399 "fillbitmap(outline, image, matrix, colortransform)\n"
400 "fillgradient(outline, gradient, gradienttype, matrix)\n"
401 "addfont(font)\n"
402 "drawchar(font, glyph, color, matrix)\n"
403 "drawlink(outline, url)\n"
404 "If any of these functions are not defined, a error message will be printed,\n"
405 "however the rendering process will *not* be aborted.\n"
406 );
407 static PyObject* f_createPassThrough(PyObject* parent, PyObject* args, PyObject* kwargs)
408 {
409     static char *kwlist[] = {"device", NULL};
410     PyObject*obj;
411     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &obj))
412         return NULL;
413     OutputObject*self = PyObject_New(OutputObject, &OutputClass);
414    
415     self->pyobj = obj;
416     self->output_device = malloc(sizeof(gfxdevice_t));
417     memset(self->output_device, 0, sizeof(gfxdevice_t));
418     self->output_device->name = strdup("passthrough");
419
420     self->output_device->setparameter = my_setparameter;
421     self->output_device->startpage = my_startpage;
422     self->output_device->startclip = my_startclip;
423     self->output_device->addfont = my_addfont;
424     self->output_device->endclip = my_endclip;
425     self->output_device->stroke = my_stroke;
426     self->output_device->fill = my_fill;
427     self->output_device->fillbitmap = my_fillbitmap;
428     self->output_device->fillgradient = my_fillgradient;
429     self->output_device->drawchar = my_drawchar;
430     self->output_device->drawlink = my_drawlink;
431     self->output_device->endpage = my_endpage;
432     self->output_device->finish = my_finish;
433     self->output_device->internal = self;
434
435     return (PyObject*)self;
436 }
437
438 static PyMethodDef output_methods[] =
439 {
440     /* Output functions */
441     {"save", (PyCFunction)output_save, METH_KEYWORDS, output_save_doc},
442     {"startpage", (PyCFunction)output_startpage, METH_KEYWORDS, output_startpage_doc},
443     {"endpage", (PyCFunction)output_endpage, METH_KEYWORDS, output_endpage_doc},
444     {"setparameter", (PyCFunction)output_setparameter, METH_KEYWORDS, output_setparameter_doc},
445     {0,0,0,0}
446 };
447
448 static void output_dealloc(PyObject* _self) {
449     OutputObject* self = (OutputObject*)_self;
450
451     if(self->output_device) {
452         gfxresult_t*result = self->output_device->finish(self->output_device);
453         if(result) {
454             result->destroy(result);result=0;
455         }
456         self->output_device = 0;
457     }
458     
459     PyObject_Del(self);
460 }
461 static PyObject* output_getattr(PyObject * _self, char* a)
462 {
463     OutputObject*self = (OutputObject*)_self;
464    
465 /*    if(!strcmp(a, "x1")) {
466         return PyInt_FromLong(self->output_device->x1);
467     } else if(!strcmp(a, "y1")) {
468         return PyInt_FromLong(self->output_device->y1);
469     } else if(!strcmp(a, "x2")) {
470         return PyInt_FromLong(self->output_device->x2);
471     } else if(!strcmp(a, "y2")) {
472         return PyInt_FromLong(self->output_device->y2);
473     }*/
474     
475     return Py_FindMethod(output_methods, _self, a);
476 }
477 static int output_setattr(PyObject * _self, char* a, PyObject * o) 
478 {
479     OutputObject*self = (OutputObject*)_self;
480     if(!PyString_Check(o))
481         return -1;
482     char*value = PyString_AsString(o);
483     self->output_device->setparameter(self->output_device, a, value);
484     return -1;
485 }
486 static int output_print(PyObject * _self, FILE *fi, int flags)
487 {
488     OutputObject*self = (OutputObject*)_self;
489     fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
490     return 0;
491 }
492
493 //---------------------------------------------------------------------
494 staticforward PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs);
495 staticforward PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs);
496
497 PyDoc_STRVAR(page_render_doc, \
498 "render(output, move=(0,0), clip=None)\n\n"
499 "Renders a page to the rendering backend specified by the output\n"
500 "parameter. Rendering consists of calling a number of functions on the\n"
501 "output device, see the description of the \"PassThrough\" device.\n"
502 "The page may be shifted to a given position using the move parameter,\n"
503 "and may also be clipped to a specific size using the clip parameter.\n"
504 "The clipping operation is applied after the move operation.\n"
505 );
506 static PyObject* page_render(PyObject* _self, PyObject* args, PyObject* kwargs)
507 {
508     PageObject* self = (PageObject*)_self; 
509     
510     static char *kwlist[] = {"dev", "move", "clip", NULL};
511     OutputObject*output = 0;
512     PyObject*move=0;
513     PyObject*clip=0;
514     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &OutputClass, &output,
515                 &move,&clip
516                 ))
517         return NULL;
518
519     int x=0,y=0;
520     int cx1=0,cy1=0,cx2=0,cy2=0;
521
522     if(move) {
523         if (!PyArg_ParseTuple(move, "ii", &x,&y))
524             return NULL;
525     }
526     if(clip) {
527         if (!PyArg_ParseTuple(clip, "iiii", &cx1,&cy1,&cx2,&cy2))
528             return NULL;
529     }
530
531     if(x|y|cx1|cx2|cy1|cy2)
532         self->page->rendersection(self->page, output->output_device,x,y,cx1,cy1,cx2,cy2);
533     else
534         self->page->render(self->page, output->output_device);
535     return PY_NONE;
536 }
537
538 PyDoc_STRVAR(page_asImage_doc, \
539 "asImage(width, height)\n\n"
540 "Creates a bitmap from a page. The bitmap will be returned as a string\n"
541 "containing RGB triplets. The bitmap will be rescaled to the specified width and\n"
542 "height. The aspect ratio of width and height doesn't need to be the same\n"
543 "as the page.\n"
544 );
545 static PyObject* page_asImage(PyObject* _self, PyObject* args, PyObject* kwargs)
546 {
547     PageObject* self = (PageObject*)_self; 
548     
549     static char *kwlist[] = {"width", "height", NULL};
550     int width=0,height=0;
551     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii", kwlist, &width, &height))
552         return NULL;
553
554     if(!width || !height) {
555         return PY_ERROR("invalid dimensions: %dx%d", width,height);
556     }
557
558     gfxdevice_t dev1,dev2;
559     gfxdevice_render_init(&dev1);
560     dev1.setparameter(&dev1, "antialise", "2");
561     gfxdevice_rescale_init(&dev2, &dev1, width, height, 0);
562     dev2.startpage(&dev2, self->page->width, self->page->height);
563     self->page->render(self->page, &dev2);
564     dev2.endpage(&dev2);
565     gfxresult_t*result = dev2.finish(&dev2);
566     gfximage_t*img = (gfximage_t*)result->get(result,"page0");
567     int l = img->width*img->height;
568     unsigned char*data = malloc(img->width*img->height*3);
569     int s,t;
570     for(t=0,s=0;t<l;s+=3,t++) {
571         data[s+0] = img->data[t].r;
572         data[s+1] = img->data[t].g;
573         data[s+2] = img->data[t].b;
574     }
575     result->destroy(result);
576     return PyString_FromStringAndSize((char*)data,img->width*img->height*3);
577 }
578
579 static PyMethodDef page_methods[] =
580 {
581     /* Page functions */
582     {"render", (PyCFunction)page_render, METH_KEYWORDS, page_render_doc},
583     {"asImage", (PyCFunction)page_asImage, METH_KEYWORDS, page_asImage_doc},
584     {0,0,0,0}
585 };
586 static void page_dealloc(PyObject* _self) {
587     PageObject* self = (PageObject*)_self; 
588     if(self->page) {
589         self->page->destroy(self->page);
590         self->page=0;
591     }
592     if(self->parent) {
593         Py_DECREF(self->parent);
594         self->parent=0;
595     }
596     
597     PyObject_Del(self);
598 }
599
600 static PyObject* page_getattr(PyObject * _self, char* a)
601 {
602     PageObject*self = (PageObject*)_self;
603     
604     if(!strcmp(a, "size")) {
605         return Py_BuildValue("(ii)", self->page->width, self->page->height);
606     } if(!strcmp(a, "doc")) {
607         Py_INCREF(self->parent);
608         return self->parent;
609     } if(!strcmp(a, "nr")) {
610         return PyInt_FromLong(self->nr);
611     } else if(!strcmp(a, "width")) {
612         return PyInt_FromLong(self->page->width);
613     } else if(!strcmp(a, "height")) {
614         return PyInt_FromLong(self->page->height);
615     }
616     return Py_FindMethod(page_methods, _self, a);
617 }
618
619 static int page_setattr(PyObject * self, char* a, PyObject * o) {
620     return -1;
621 }
622 static int page_print(PyObject * _self, FILE *fi, int flags)
623 {
624     PageObject*self = (PageObject*)_self;
625     fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
626     return 0;
627 }
628
629 //---------------------------------------------------------------------
630
631 PyDoc_STRVAR(doc_getPage_doc,
632 "getPage(nr)\n\n"
633 "Get one page from a document file. The nr parameter specifies\n"
634 "which page to retrieve. Counting starts at 1, so the first page\n"
635 "can be retrieved by\n"
636 "    page = doc.getPage(1)\n"
637 ".\n"
638 "You can find out how many pages a document contains by querying\n"
639 "its pages field (doc.pages)\n"
640 );
641 static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
642 {
643     DocObject* self = (DocObject*)_self;
644
645     static char *kwlist[] = {"nr", NULL};
646     int pagenr = 0;
647     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pagenr))
648         return NULL;
649
650     PageObject*page = PyObject_New(PageObject, &PageClass);
651     page->page = self->doc->getpage(self->doc, pagenr);
652     page->nr = pagenr;
653     page->parent = _self;
654     Py_INCREF(page->parent);
655     if(!page->page) {
656         PyObject_Del(page);
657         return PY_ERROR("Couldn't extract page %d", pagenr);
658     }
659     return (PyObject*)page;
660 }
661
662 PyDoc_STRVAR(doc_getInfo_doc,
663 "getInfo(key)\n\n"
664 "Retrieve some information about a document. For PDF files, key\n"
665 "can have the following values:\n\n"
666 "\"title\", \"subject\", \"keywords\", \"author\", \"creator\", \"producer\",\n"
667 "\"creationdate\", \"moddate\", \"linearized\", \"tagged\", \"encrypted\",\n"
668 "\"oktoprint\", \"oktocopy\", \"oktochange\", \"oktoaddnotes\", \"version\".\n\n"
669 "If the \"oktocopy\" digital rights management flag is set to \"no\", then the\n"
670 "pdf parser won't allow you to access the PDF file. Trying to extract pages\n"
671 "from it will raise an exception.\n"
672 );
673 static PyObject* doc_getInfo(PyObject* _self, PyObject* args, PyObject* kwargs)
674 {
675     DocObject* self = (DocObject*)_self;
676
677     static char *kwlist[] = {"key", NULL};
678     char*key = 0;
679     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &key))
680         return NULL;
681
682     char*s = self->doc->getinfo(self->doc, key);
683     return PyString_FromString(s);
684 }
685
686 PyDoc_STRVAR(doc_setparameter_doc,
687 "setparameter(key, value)\n\n"
688 "Pass a parameter or setting to the document parser. Unlike\n"
689 "the module level setparameter() function, the parameters set\n"
690 "using setparameter will only be valid for the object itself\n"
691 "during its lifetime.\n"
692 );
693 static PyObject* doc_setparameter(PyObject* _self, PyObject* args, PyObject* kwargs)
694 {
695     DocObject* self = (DocObject*)_self;
696
697     static char *kwlist[] = {"key", "value", NULL};
698     char*key = 0, *value=0;
699     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key,&value))
700         return NULL;
701
702     self->doc->set_parameter(self->doc, key, value);
703     return PY_NONE;
704 }
705
706 PyDoc_STRVAR(f_open_doc,
707 "open(type, filename) -> object\n\n"
708 "Open a PDF, SWF or image file. The type argument should be \"pdf\",\n"
709 "\"swf\" or \"image\" accordingly. It returns a doc object which can be\n"
710 "used to process the file contents.\n"
711 "E.g.\n"
712 "    doc = open(\"pdf\", \"document.pdf\")\n"
713 "    doc = open(\"swf\", \"flashfile.swf\")\n"
714 "    doc = open(\"image\", \"image.png\")\n"
715 "If the file could not be loaded, or is a encrypted PDF file without\n"
716 "a proper password specified, an exception is being raised.\n"
717 "If the filename argument contains a '|' char, everything behind\n"
718 "the '|' is treated as password used for opening the file.\n"
719 "E.g.\n"
720 "    doc = open(\"pdf\", \"document.pdf|mysecretpassword\")\n"
721 ".\n"
722 "Notice that for image files, the only supported file formats right now\n"
723 "are jpeg and png.\n"
724 );
725 static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
726 {
727     static char *kwlist[] = {"type", "filename", NULL};
728     char*filename=0;
729     char*type=0;
730     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
731         static char *kwlist2[] = {"filename", NULL};
732         type = 0;
733         PyErr_Clear();
734         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist2, &filename))
735             return NULL;
736     }
737
738     DocObject*self = PyObject_New(DocObject, &DocClass);
739
740     if(!type) { //autodetect
741         type = "pdf"; //default
742         int l = strlen(filename);
743         if(l>4) {
744             if(filename[l-4]=='.') {
745                 if(strchr("pP", filename[l-3]) && strchr("dD", filename[l-2]) && strchr("fF", filename[l-1]))
746                     type = "pdf";
747                 if(strchr("jJ", filename[l-3]) && strchr("pP", filename[l-2]) && strchr("gG", filename[l-1]))
748                     type = "image";
749                 if(strchr("pP", filename[l-3]) && strchr("nN", filename[l-2]) && strchr("gG", filename[l-1]))
750                     type = "image";
751                 if(strchr("sS", filename[l-3]) && strchr("wW", filename[l-2]) && strchr("fF", filename[l-1]))
752                     type = "swf";
753             } else if(filename[l-5]=='.') {
754                 type = "image";
755             }
756         }
757     }
758    
759     if(!strcmp(type,"pdf"))
760         self->doc = pdfdriver->open(pdfdriver,filename);
761     else if(!strcmp(type, "image") || !strcmp(type, "img"))  
762         self->doc = imagedriver->open(imagedriver, filename);
763     else if(!strcmp(type, "swf") || !strcmp(type, "SWF"))
764         self->doc = swfdriver->open(imagedriver, filename);
765     else
766         return PY_ERROR("Unknown type %s", type);
767
768     if(!self->doc) {
769         PyObject_Del(self);
770         return PY_ERROR("Couldn't open %s", filename);
771     }
772     self->filename = strdup(filename);
773     return (PyObject*)self;
774 }
775
776 static PyMethodDef doc_methods[] =
777 {
778     /* PDF functions */
779     {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, doc_getPage_doc},
780     {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, doc_getInfo_doc},
781     {"setparameter", (PyCFunction)doc_setparameter, METH_KEYWORDS, doc_setparameter_doc},
782     {0,0,0,0}
783 };
784
785 static void doc_dealloc(PyObject* _self) {
786     DocObject* self = (DocObject*)_self;
787     if(self->doc) {
788         self->doc->destroy(self->doc);
789         self->doc=0;
790     }
791     if(self->filename) {
792         free(self->filename);self->filename=0;
793     }
794     PyObject_Del(self);
795 }
796 static PyObject* doc_getattr(PyObject * _self, char* a)
797 {
798     DocObject*self = (DocObject*)_self;
799     if(!strcmp(a, "pages")) {
800         return PyInt_FromLong(self->doc->num_pages);
801     }
802     if(!strcmp(a, "filename")) {
803         return PyString_FromString(self->filename);
804     }
805     return Py_FindMethod(doc_methods, _self, a);
806 }
807 static int doc_setattr(PyObject * self, char* a, PyObject * o) {
808     return -1;
809 }
810 static int doc_print(PyObject * _self, FILE *fi, int flags)
811 {
812     DocObject*self = (DocObject*)_self;
813     fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
814     return 0;
815 }
816
817 //---------------------------------------------------------------------
818
819 PyDoc_STRVAR(output_doc,
820 "An Output object can be used as parameter to the render()\n"
821 "call of a page. It's not possible to create this type of\n"
822 "object directly (i.e., from a class), however you can\n"
823 "use a PassThrough() device to pass things over to Python.\n"
824 "Examples for classes implementing the Output class are: \n"
825 "ImageList, SWF, PlainText and PassThrough.\n"
826 );
827 static PyTypeObject OutputClass =
828 {
829     PyObject_HEAD_INIT(NULL)
830     0,
831     tp_name: "gfx.Output",
832     tp_basicsize: sizeof(OutputObject),
833     tp_itemsize: 0,
834     tp_dealloc: output_dealloc,
835     tp_print: output_print,
836     tp_getattr: output_getattr,
837     tp_setattr: output_setattr,
838     tp_doc: output_doc,
839     tp_methods: output_methods
840 };
841 PyDoc_STRVAR(page_doc,
842 "A Page object contains a single page of a document.\n"
843 "page.width and page.height (or page.size) contain the\n"
844 "page dimensions. page.nr is the number of the page, and\n"
845 "page.doc is the parent document.\n"
846 );
847 static PyTypeObject PageClass =
848 {
849     PyObject_HEAD_INIT(NULL)
850     0,
851     tp_name: "gfx.Page",
852     tp_basicsize: sizeof(PageObject),
853     tp_itemsize: 0,
854     tp_dealloc: page_dealloc,
855     tp_print: page_print,
856     tp_getattr: page_getattr,
857     tp_setattr: page_setattr,
858     tp_doc: page_doc,
859     tp_methods: page_methods
860 };
861 PyDoc_STRVAR(doc_doc,
862 "A Doc object is used for storing a document (like a PDF).\n"
863 "doc.pages contains the number of pages in the document,\n"
864 "and doc.filename the name of the file the document was\n"
865 "created (loaded) from. If the document was created from\n"
866 "an image file, the number of pages is always 1\n"
867 );
868 static PyTypeObject DocClass =
869 {
870     PyObject_HEAD_INIT(NULL)
871     0,
872     tp_name: "gfx.Doc",
873     tp_basicsize: sizeof(DocObject),
874     tp_itemsize: 0,
875     tp_dealloc: doc_dealloc,
876     tp_print: doc_print,
877     tp_getattr: doc_getattr,
878     tp_setattr: doc_setattr,
879     tp_doc: doc_doc,
880     tp_methods: doc_methods,
881 };
882
883 //=====================================================================
884
885 PyDoc_STRVAR(f_setparameter_doc, \
886 "setparameter(key,value)\n\n"
887 "Set a parameter in the gfx module (which might affect the PDF\n"
888 "parser or any of the rendering backends). This is a parameter\n"
889 "which would usually be passed with the \"-s\" option to pdf2swf.\n"
890 "For a list of all parameters, see the output of\n"
891 "    pdf2swf -s help\n"
892 "and\n"
893 "    pdf2swf somefile.pdf -s help\n"
894 ".\n"
895 );
896 static PyObject* f_setparameter(PyObject* self, PyObject* args, PyObject* kwargs)
897 {
898     static char *kwlist[] = {"key", "value", NULL};
899     char*key=0,*value=0;
900     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
901         return NULL;
902     pdfdriver->set_parameter(pdfdriver,key,value);
903     return PY_NONE;
904 }
905
906 PyDoc_STRVAR(f_verbose_doc, \
907 "verbose(level)\n\n"
908 "Set the logging verbosity of the gfx module. Log levels are:\n"
909 "level=-1          Log nothing\n"
910 "level=0 (fatal)   Log only fatal errors\n"
911 "level=1 (error)   Log only fatal errors and errors\n"
912 "level=2 (warn)    Log all errors and warnings\n"
913 "level=3 (notice)  Log also some rudimentary data about the parsing/conversion\n"
914 "level=4 (verbose) Log some additional parsing information\n"
915 "level=5 (debug)   Log debug statements\n"
916 "level=6 (trace)   Log extended debug statements\n"
917 "All logging messages are written to stdout.\n"
918 );
919 static PyObject* f_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
920 {
921     static char *kwlist[] = {"val", NULL};
922     int val;
923     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &val))
924         return NULL;
925     setConsoleLogging(val);
926     return PY_NONE;
927 }
928
929 PyDoc_STRVAR(f_addfont_doc, \
930 "addfont(filename)\n\n"
931 "Passes an additional font file to the PDF parser. If a PDF contains\n"
932 "external fonts (i.e. fonts which are not contained in the PDF itself)\n"
933 "then the files added by addfont() will be searched.\n"
934 );
935
936 static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
937 {
938     static char *kwlist[] = {"filename", NULL};
939     char*filename=0;
940     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
941         return NULL;
942     pdfdriver->set_parameter(pdfdriver,"font", filename);
943     return PY_NONE;
944 }
945
946 PyDoc_STRVAR(f_addfontdir_doc, \
947 "addfontdir(dirname)\n\n"
948 "Passes a complete directory containing fonts to the PDF parser. Any\n"
949 "font file within this directory might be used to resolve external fonts\n"
950 "in PDF files\n"
951 );
952 static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
953 {
954     static char *kwlist[] = {"filename", NULL};
955     char*filename=0;
956     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
957         return NULL;
958     pdfdriver->set_parameter(pdfdriver,"fontdir", filename);
959     return PY_NONE;
960 }
961
962 static PyMethodDef pdf2swf_methods[] =
963 {
964     /* sources */
965     {"open", (PyCFunction)f_open, METH_KEYWORDS, f_open_doc},
966     {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, f_addfont_doc},
967     {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, f_addfontdir_doc},
968     {"setparameter", (PyCFunction)f_setparameter, METH_KEYWORDS, f_setparameter_doc},
969     {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, f_verbose_doc},
970
971     /* devices */
972     {"SWF", (PyCFunction)f_createSWF, METH_KEYWORDS, f_createSWF_doc},
973     {"OCR", (PyCFunction)f_createOCR, METH_KEYWORDS, f_createOCR_doc},
974     {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, f_createImageList_doc},
975     {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, f_createPlainText_doc},
976     {"PassThrough", (PyCFunction)f_createPassThrough, METH_KEYWORDS, f_createPassThrough_doc},
977
978     /* sentinel */
979     {0, 0, 0, 0}
980 };
981
982 PyDoc_STRVAR(gfx_doc, \
983 "This module contains a PDF parser (based on xpdf) and a number of\n"
984 "rendering backends. In particular, it can extract text from PDF pages,\n"
985 "create bitmaps from them, or convert PDF files to SWF.\n" 
986 "The latter functionality is similar to what is offered by swftools'\n" 
987 "(http://www.swftools.org) pdf2swf utility, however more powerful-\n" 
988 "You can also create individual SWF files from single pages of the PDF\n" 
989 "or mix pages from different PDF files.\n"
990 );
991
992 void initgfx(void)
993 {
994     initLog(0,0,0,0,0,2);
995     OutputClass.ob_type = &PyType_Type;
996     PageClass.ob_type = &PyType_Type;
997     DocClass.ob_type = &PyType_Type;
998
999     pdfdriver = gfxsource_pdf_create();
1000     swfdriver = gfxsource_swf_create();
1001     imagedriver = gfxsource_image_create();
1002     
1003     PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
1004     PyObject*module_dict = PyModule_GetDict(module);
1005
1006     PyDict_SetItemString(module_dict, "Doc", (PyObject*)&DocClass);
1007     PyDict_SetItemString(module_dict, "Page", (PyObject*)&PageClass);
1008     PyDict_SetItemString(module_dict, "Output", (PyObject*)&OutputClass);
1009 }