fixed various typos in the 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 *not* be aborted.\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 "Get one page from a document file. The nr parameter specifies\n"
593 "which page to retrieve. Counting starts at 1, so the first page\n"
594 "can be retrieved by\n"
595 "    page = doc.getPage(1)\n"
596 ".\n"
597 "You can find out how many pages a document contains by querying\n"
598 "its pages field (doc.pages)\n"
599 );
600 static PyObject* doc_getPage(PyObject* _self, PyObject* args, PyObject* kwargs)
601 {
602     DocObject* self = (DocObject*)_self;
603
604     static char *kwlist[] = {"nr", NULL};
605     int pagenr = 0;
606     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &pagenr))
607         return NULL;
608
609     PageObject*page = PyObject_New(PageObject, &PageClass);
610     page->page = self->doc->getpage(self->doc, pagenr);
611     page->nr = pagenr;
612     page->parent = _self;
613     Py_INCREF(page->parent);
614     if(!page->page) {
615         PyObject_Del(page);
616         return PY_ERROR("Couldn't extract page %d", pagenr);
617     }
618     return (PyObject*)page;
619 }
620
621 PyDoc_STRVAR(doc_getInfo_doc,
622 "getInfo(key)\n\n"
623 "Retrieve some information about a document. For PDF files, key\n"
624 "can have the following values:\n\n"
625 "\"title\", \"subject\", \"keywords\", \"author\", \"creator\", \"producer\",\n"
626 "\"creationdate\", \"moddate\", \"linearized\", \"tagged\", \"encrypted\",\n"
627 "\"oktoprint\", \"oktocopy\", \"oktochange\", \"oktoaddnotes\", \"version\".\n\n"
628 "If the \"oktocopy\" digital rights management flag is set to \"no\", then the\n"
629 "pdf parser won't allow you to access the PDF file. Trying to extract pages\n"
630 "from it will raise an exception.\n"
631 );
632 static PyObject* doc_getInfo(PyObject* _self, PyObject* args, PyObject* kwargs)
633 {
634     DocObject* self = (DocObject*)_self;
635
636     static char *kwlist[] = {"key", NULL};
637     char*key = 0;
638     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &key))
639         return NULL;
640
641     char*s = self->doc->getinfo(self->doc, key);
642     return PyString_FromString(s);
643 }
644
645 PyDoc_STRVAR(doc_setParameter_doc,
646 "setParameter(key, value)\n\n"
647 "Pass a parameter or setting to the document parser. Unlike\n"
648 "the module level setoption() function, the parameters set\n"
649 "using setParameter will only be valid for the object itself\n"
650 "during its lifetime.\n"
651 );
652 static PyObject* doc_setParameter(PyObject* _self, PyObject* args, PyObject* kwargs)
653 {
654     DocObject* self = (DocObject*)_self;
655
656     static char *kwlist[] = {"key", "value", NULL};
657     char*key = 0, *value=0;
658     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key,&value))
659         return NULL;
660
661     self->doc->set_parameter(self->doc, key, value);
662     return PY_NONE;
663 }
664
665 PyDoc_STRVAR(f_open_doc,
666 "open(type, filename) -> object\n\n"
667 "Open a PDF file. The type argument always has to be \"pdf\"\n"
668 "It returns a doc object which can be used to process the pdf\n"
669 "contents. E.g.\n"
670 "    doc = open(\"pdf\", \"document.pdf\")\n"
671 "If the file is not a PDF file or is encrypted without\n"
672 "a proper password specified, an exception is being raised.\n"
673 "If the filename argument contains a '|' char, everything behind\n"
674 "the '|' is treated as password used for opening the file.\n"
675 "E.g.\n"
676 "    doc = open(\"pdf\", \"document.pdf|mysecretpassword\")\n"
677 );
678 static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
679 {
680     static char *kwlist[] = {"type", "filename", NULL};
681     char*filename;
682     char*type;
683     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
684         type = "pdf";
685         PyErr_Clear();
686         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
687             return NULL;
688     }
689
690     DocObject*self = PyObject_New(DocObject, &DocClass);
691    
692     if(!strcmp(type,"pdf"))
693         self->doc = pdfdriver->open(pdfdriver,filename);
694     else
695         return PY_ERROR("Unknown type %s", type);
696
697     if(!self->doc) {
698         PyObject_Del(self);
699         return PY_ERROR("Couldn't open %s", filename);
700     }
701     self->filename = strdup(filename);
702     return (PyObject*)self;
703 }
704
705 static PyMethodDef doc_methods[] =
706 {
707     /* PDF functions */
708     {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, doc_getPage_doc},
709     {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, doc_getInfo_doc},
710     {"setParameter", (PyCFunction)doc_setParameter, METH_KEYWORDS, doc_setParameter_doc},
711     {0,0,0,0}
712 };
713
714 static void doc_dealloc(PyObject* _self) {
715     DocObject* self = (DocObject*)_self;
716     if(self->doc) {
717         self->doc->destroy(self->doc);
718         self->doc=0;
719     }
720     if(self->filename) {
721         free(self->filename);self->filename=0;
722     }
723     PyObject_Del(self);
724 }
725 static PyObject* doc_getattr(PyObject * _self, char* a)
726 {
727     DocObject*self = (DocObject*)_self;
728     if(!strcmp(a, "pages")) {
729         return PyInt_FromLong(self->doc->num_pages);
730     }
731     if(!strcmp(a, "filename")) {
732         return PyString_FromString(self->filename);
733     }
734     return Py_FindMethod(doc_methods, _self, a);
735 }
736 static int doc_setattr(PyObject * self, char* a, PyObject * o) {
737     return -1;
738 }
739 static int doc_print(PyObject * _self, FILE *fi, int flags)
740 {
741     DocObject*self = (DocObject*)_self;
742     fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
743     return 0;
744 }
745
746 //---------------------------------------------------------------------
747
748 PyDoc_STRVAR(output_doc,
749 "An Output object can be used as parameter to the render()\n"
750 "call of a page. It's not possible to create this type of\n"
751 "object directly (i.e., from a class), however you can\n"
752 "use a PassThrough() device to pass things over to Python.\n"
753 "Examples for classes implementing the Output class are: \n"
754 "ImageList, SWF, PlainText and PassThrough.\n"
755 );
756 static PyTypeObject OutputClass =
757 {
758     PyObject_HEAD_INIT(NULL)
759     0,
760     tp_name: "gfx.Output",
761     tp_basicsize: sizeof(OutputObject),
762     tp_itemsize: 0,
763     tp_dealloc: output_dealloc,
764     tp_print: output_print,
765     tp_getattr: output_getattr,
766     tp_setattr: output_setattr,
767     tp_doc: output_doc,
768     tp_methods: output_methods
769 };
770 PyDoc_STRVAR(page_doc,
771 "A Page object contains a single page of a document.\n"
772 "page.width and page.height (or page.size) contain the\n"
773 "page dimensions. page.nr is the number of the page, and\n"
774 "page.doc is the parent document.\n"
775 );
776 static PyTypeObject PageClass =
777 {
778     PyObject_HEAD_INIT(NULL)
779     0,
780     tp_name: "gfx.Page",
781     tp_basicsize: sizeof(PageObject),
782     tp_itemsize: 0,
783     tp_dealloc: page_dealloc,
784     tp_print: page_print,
785     tp_getattr: page_getattr,
786     tp_setattr: page_setattr,
787     tp_doc: page_doc,
788     tp_methods: page_methods
789 };
790 PyDoc_STRVAR(doc_doc,
791 "A Doc object is used for storing a document (like a PDF).\n"
792 "doc.pages contains the number of pages in the document,\n"
793 "and doc.filename the name of the file the document was\n"
794 "created (loaded) from\n"
795 );
796 static PyTypeObject DocClass =
797 {
798     PyObject_HEAD_INIT(NULL)
799     0,
800     tp_name: "gfx.Doc",
801     tp_basicsize: sizeof(DocObject),
802     tp_itemsize: 0,
803     tp_dealloc: doc_dealloc,
804     tp_print: doc_print,
805     tp_getattr: doc_getattr,
806     tp_setattr: doc_setattr,
807     tp_doc: doc_doc,
808     tp_methods: doc_methods,
809 };
810
811 //=====================================================================
812
813 PyDoc_STRVAR(f_setoption_doc, \
814 "setoption(key,value)\n\n"
815 "Set a parameter in the gfx module (which might affect the PDF\n"
816 "parser or any of the rendering backends). This is a parameter\n"
817 "which would usually be passed with the \"-s\" option to pdf2swf.\n"
818 "For a list of all parameters, see the output of\n"
819 "    pdf2swf -s help\n"
820 "and\n"
821 "    pdf2swf somefile.pdf -s help\n"
822 ".\n"
823 );
824 static PyObject* f_setoption(PyObject* self, PyObject* args, PyObject* kwargs)
825 {
826     static char *kwlist[] = {"key", "value", NULL};
827     char*key=0,*value=0;
828     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
829         return NULL;
830     pdfdriver->set_parameter(pdfdriver,key,value);
831     return PY_NONE;
832 }
833
834 PyDoc_STRVAR(f_verbose_doc, \
835 "verbose(level)\n\n"
836 "Set the logging verbosity of the gfx module. Log levels are:\n"
837 "level=-1          Log nothing\n"
838 "level=0 (fatal)   Log only fatal errors\n"
839 "level=1 (error)   Log only fatal errors and errors\n"
840 "level=2 (warn)    Log all errors and warnings\n"
841 "level=3 (notice)  Log also some rudimentary data about the parsing/conversion\n"
842 "level=4 (verbose) Log some additional parsing information\n"
843 "level=5 (debug)   Log debug statements\n"
844 "level=6 (trace)   Log extended debug statements\n"
845 "All logging messages are written to stdout.\n"
846 );
847 static PyObject* f_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
848 {
849     static char *kwlist[] = {"val", NULL};
850     int val;
851     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &val))
852         return NULL;
853     setConsoleLogging(val);
854     return PY_NONE;
855 }
856
857 PyDoc_STRVAR(f_addfont_doc, \
858 "addfont(filename)\n\n"
859 "Passes an additional font file to the PDF parser. If a PDF contains\n"
860 "external fonts (i.e. fonts which are not contained in the PDF itself)\n"
861 "then the files added by addfont() will be searched.\n"
862 );
863
864 static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
865 {
866     static char *kwlist[] = {"filename", NULL};
867     char*filename=0;
868     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
869         return NULL;
870     pdfdriver->set_parameter(pdfdriver,"font", filename);
871     return PY_NONE;
872 }
873
874 PyDoc_STRVAR(f_addfontdir_doc, \
875 "addfontdir(dirname)\n\n"
876 "Passes a complete directory containing fonts to the PDF parser. Any\n"
877 "font file within this directory might be used to resolve external fonts\n"
878 "in PDF files\n"
879 );
880 static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
881 {
882     static char *kwlist[] = {"filename", NULL};
883     char*filename=0;
884     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
885         return NULL;
886     pdfdriver->set_parameter(pdfdriver,"fontdir", filename);
887     return PY_NONE;
888 }
889
890 static PyMethodDef pdf2swf_methods[] =
891 {
892     /* sources */
893     {"open", (PyCFunction)f_open, METH_KEYWORDS, f_open_doc},
894     {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, f_addfont_doc},
895     {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, f_addfontdir_doc},
896     {"setoption", (PyCFunction)f_setoption, METH_KEYWORDS, f_setoption_doc},
897     {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, f_verbose_doc},
898
899     /* devices */
900     {"SWF", (PyCFunction)f_createSWF, METH_KEYWORDS, f_createSWF_doc},
901     {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, f_createImageList_doc},
902     {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, f_createPlainText_doc},
903     {"PassThrough", (PyCFunction)f_createPassThrough, METH_KEYWORDS, f_createPassThrough_doc},
904
905     /* sentinel */
906     {0, 0, 0, 0}
907 };
908
909 PyDoc_STRVAR(gfx_doc, \
910 "This module contains a PDF parser (based on xpdf) and a number of\n"
911 "rendering backends. In particular, it can extract text from PDF pages,\n"
912 "create bitmaps from them, or convert PDF files to SWF.\n" 
913 "The latter functionality is similar to what is offered by swftools'\n" 
914 "(http://www.swftools.org) pdf2swf utility, however more powerful-\n" 
915 "You can also create individual SWF files from single pages of the PDF\n" 
916 "or combine more than one page into a bigger PDF.\n"
917 );
918
919 void initgfx(void)
920 {
921     initLog(0,0,0,0,0,2);
922     OutputClass.ob_type = &PyType_Type;
923     PageClass.ob_type = &PyType_Type;
924     DocClass.ob_type = &PyType_Type;
925
926     pdfdriver = gfxsource_pdf_create();
927     
928     PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
929     PyObject*module_dict = PyModule_GetDict(module);
930
931     PyDict_SetItemString(module_dict, "Doc", (PyObject*)&DocClass);
932     PyDict_SetItemString(module_dict, "Page", (PyObject*)&PageClass);
933     PyDict_SetItemString(module_dict, "Output", (PyObject*)&OutputClass);
934 }