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