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