added more drivers
[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 file. The type argument always has to be \"pdf\"\n"
687 "It returns a doc object which can be used to process the pdf\n"
688 "contents. E.g.\n"
689 "    doc = open(\"pdf\", \"document.pdf\")\n"
690 "If the file is not a PDF file or is encrypted without\n"
691 "a proper password specified, an exception is being raised.\n"
692 "If the filename argument contains a '|' char, everything behind\n"
693 "the '|' is treated as password used for opening the file.\n"
694 "E.g.\n"
695 "    doc = open(\"pdf\", \"document.pdf|mysecretpassword\")\n"
696 );
697 static PyObject* f_open(PyObject* parent, PyObject* args, PyObject* kwargs)
698 {
699     static char *kwlist[] = {"type", "filename", NULL};
700     char*filename=0;
701     char*type=0;
702     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &type, &filename)) {
703         static char *kwlist2[] = {"filename", NULL};
704         type = 0;
705         PyErr_Clear();
706         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist2, &filename))
707             return NULL;
708     }
709
710     DocObject*self = PyObject_New(DocObject, &DocClass);
711
712     if(!type) { //autodetect
713         type = "pdf"; //default
714         int l = strlen(filename);
715         if(l>4) {
716             if(filename[l-4]=='.') {
717                 if(strchr("pP", filename[l-3]) && strchr("dD", filename[l-2]) && strchr("fF", filename[l-1]))
718                     type = "pdf";
719                 if(strchr("jJ", filename[l-3]) && strchr("pP", filename[l-2]) && strchr("gG", filename[l-1]))
720                     type = "image";
721                 if(strchr("pP", filename[l-3]) && strchr("nN", filename[l-2]) && strchr("gG", filename[l-1]))
722                     type = "image";
723             } else if(filename[l-5]=='.') {
724                 type = "image";
725             }
726         }
727     }
728    
729     if(!strcmp(type,"pdf"))
730         self->doc = pdfdriver->open(pdfdriver,filename);
731     else if(!strcmp(type, "image")) 
732         self->doc = imagedriver->open(imagedriver, filename);
733     else if(!strcmp(type, "swf")) 
734         self->doc = swfdriver->open(imagedriver, filename);
735     else
736         return PY_ERROR("Unknown type %s", type);
737
738     if(!self->doc) {
739         PyObject_Del(self);
740         return PY_ERROR("Couldn't open %s", filename);
741     }
742     self->filename = strdup(filename);
743     return (PyObject*)self;
744 }
745
746 static PyMethodDef doc_methods[] =
747 {
748     /* PDF functions */
749     {"getPage", (PyCFunction)doc_getPage, METH_KEYWORDS, doc_getPage_doc},
750     {"getInfo", (PyCFunction)doc_getInfo, METH_KEYWORDS, doc_getInfo_doc},
751     {"setparameter", (PyCFunction)doc_setparameter, METH_KEYWORDS, doc_setparameter_doc},
752     {0,0,0,0}
753 };
754
755 static void doc_dealloc(PyObject* _self) {
756     DocObject* self = (DocObject*)_self;
757     if(self->doc) {
758         self->doc->destroy(self->doc);
759         self->doc=0;
760     }
761     if(self->filename) {
762         free(self->filename);self->filename=0;
763     }
764     PyObject_Del(self);
765 }
766 static PyObject* doc_getattr(PyObject * _self, char* a)
767 {
768     DocObject*self = (DocObject*)_self;
769     if(!strcmp(a, "pages")) {
770         return PyInt_FromLong(self->doc->num_pages);
771     }
772     if(!strcmp(a, "filename")) {
773         return PyString_FromString(self->filename);
774     }
775     return Py_FindMethod(doc_methods, _self, a);
776 }
777 static int doc_setattr(PyObject * self, char* a, PyObject * o) {
778     return -1;
779 }
780 static int doc_print(PyObject * _self, FILE *fi, int flags)
781 {
782     DocObject*self = (DocObject*)_self;
783     fprintf(fi, "%08x(%d)", (int)_self, _self?_self->ob_refcnt:0);
784     return 0;
785 }
786
787 //---------------------------------------------------------------------
788
789 PyDoc_STRVAR(output_doc,
790 "An Output object can be used as parameter to the render()\n"
791 "call of a page. It's not possible to create this type of\n"
792 "object directly (i.e., from a class), however you can\n"
793 "use a PassThrough() device to pass things over to Python.\n"
794 "Examples for classes implementing the Output class are: \n"
795 "ImageList, SWF, PlainText and PassThrough.\n"
796 );
797 static PyTypeObject OutputClass =
798 {
799     PyObject_HEAD_INIT(NULL)
800     0,
801     tp_name: "gfx.Output",
802     tp_basicsize: sizeof(OutputObject),
803     tp_itemsize: 0,
804     tp_dealloc: output_dealloc,
805     tp_print: output_print,
806     tp_getattr: output_getattr,
807     tp_setattr: output_setattr,
808     tp_doc: output_doc,
809     tp_methods: output_methods
810 };
811 PyDoc_STRVAR(page_doc,
812 "A Page object contains a single page of a document.\n"
813 "page.width and page.height (or page.size) contain the\n"
814 "page dimensions. page.nr is the number of the page, and\n"
815 "page.doc is the parent document.\n"
816 );
817 static PyTypeObject PageClass =
818 {
819     PyObject_HEAD_INIT(NULL)
820     0,
821     tp_name: "gfx.Page",
822     tp_basicsize: sizeof(PageObject),
823     tp_itemsize: 0,
824     tp_dealloc: page_dealloc,
825     tp_print: page_print,
826     tp_getattr: page_getattr,
827     tp_setattr: page_setattr,
828     tp_doc: page_doc,
829     tp_methods: page_methods
830 };
831 PyDoc_STRVAR(doc_doc,
832 "A Doc object is used for storing a document (like a PDF).\n"
833 "doc.pages contains the number of pages in the document,\n"
834 "and doc.filename the name of the file the document was\n"
835 "created (loaded) from\n"
836 );
837 static PyTypeObject DocClass =
838 {
839     PyObject_HEAD_INIT(NULL)
840     0,
841     tp_name: "gfx.Doc",
842     tp_basicsize: sizeof(DocObject),
843     tp_itemsize: 0,
844     tp_dealloc: doc_dealloc,
845     tp_print: doc_print,
846     tp_getattr: doc_getattr,
847     tp_setattr: doc_setattr,
848     tp_doc: doc_doc,
849     tp_methods: doc_methods,
850 };
851
852 //=====================================================================
853
854 PyDoc_STRVAR(f_setparameter_doc, \
855 "setparameter(key,value)\n\n"
856 "Set a parameter in the gfx module (which might affect the PDF\n"
857 "parser or any of the rendering backends). This is a parameter\n"
858 "which would usually be passed with the \"-s\" option to pdf2swf.\n"
859 "For a list of all parameters, see the output of\n"
860 "    pdf2swf -s help\n"
861 "and\n"
862 "    pdf2swf somefile.pdf -s help\n"
863 ".\n"
864 );
865 static PyObject* f_setparameter(PyObject* self, PyObject* args, PyObject* kwargs)
866 {
867     static char *kwlist[] = {"key", "value", NULL};
868     char*key=0,*value=0;
869     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss", kwlist, &key, &value))
870         return NULL;
871     pdfdriver->set_parameter(pdfdriver,key,value);
872     return PY_NONE;
873 }
874
875 PyDoc_STRVAR(f_verbose_doc, \
876 "verbose(level)\n\n"
877 "Set the logging verbosity of the gfx module. Log levels are:\n"
878 "level=-1          Log nothing\n"
879 "level=0 (fatal)   Log only fatal errors\n"
880 "level=1 (error)   Log only fatal errors and errors\n"
881 "level=2 (warn)    Log all errors and warnings\n"
882 "level=3 (notice)  Log also some rudimentary data about the parsing/conversion\n"
883 "level=4 (verbose) Log some additional parsing information\n"
884 "level=5 (debug)   Log debug statements\n"
885 "level=6 (trace)   Log extended debug statements\n"
886 "All logging messages are written to stdout.\n"
887 );
888 static PyObject* f_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
889 {
890     static char *kwlist[] = {"val", NULL};
891     int val;
892     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &val))
893         return NULL;
894     setConsoleLogging(val);
895     return PY_NONE;
896 }
897
898 PyDoc_STRVAR(f_addfont_doc, \
899 "addfont(filename)\n\n"
900 "Passes an additional font file to the PDF parser. If a PDF contains\n"
901 "external fonts (i.e. fonts which are not contained in the PDF itself)\n"
902 "then the files added by addfont() will be searched.\n"
903 );
904
905 static PyObject* f_addfont(PyObject* self, PyObject* args, PyObject* kwargs)
906 {
907     static char *kwlist[] = {"filename", NULL};
908     char*filename=0;
909     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
910         return NULL;
911     pdfdriver->set_parameter(pdfdriver,"font", filename);
912     return PY_NONE;
913 }
914
915 PyDoc_STRVAR(f_addfontdir_doc, \
916 "addfontdir(dirname)\n\n"
917 "Passes a complete directory containing fonts to the PDF parser. Any\n"
918 "font file within this directory might be used to resolve external fonts\n"
919 "in PDF files\n"
920 );
921 static PyObject* f_addfontdir(PyObject* self, PyObject* args, PyObject* kwargs)
922 {
923     static char *kwlist[] = {"filename", NULL};
924     char*filename=0;
925     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &filename))
926         return NULL;
927     pdfdriver->set_parameter(pdfdriver,"fontdir", filename);
928     return PY_NONE;
929 }
930
931 static PyMethodDef pdf2swf_methods[] =
932 {
933     /* sources */
934     {"open", (PyCFunction)f_open, METH_KEYWORDS, f_open_doc},
935     {"addfont", (PyCFunction)f_addfont, METH_KEYWORDS, f_addfont_doc},
936     {"addfontdir", (PyCFunction)f_addfontdir, METH_KEYWORDS, f_addfontdir_doc},
937     {"setoption", (PyCFunction)f_setparameter, METH_KEYWORDS, f_setparameter_doc}, // for backwards-compatibility
938     {"setparameter", (PyCFunction)f_setparameter, METH_KEYWORDS, f_setparameter_doc},
939     {"verbose", (PyCFunction)f_verbose, METH_KEYWORDS, f_verbose_doc},
940
941     /* devices */
942     {"SWF", (PyCFunction)f_createSWF, METH_KEYWORDS, f_createSWF_doc},
943     {"ImageList", (PyCFunction)f_createImageList, METH_KEYWORDS, f_createImageList_doc},
944     {"PlainText", (PyCFunction)f_createPlainText, METH_KEYWORDS, f_createPlainText_doc},
945     {"PassThrough", (PyCFunction)f_createPassThrough, METH_KEYWORDS, f_createPassThrough_doc},
946
947     /* sentinel */
948     {0, 0, 0, 0}
949 };
950
951 PyDoc_STRVAR(gfx_doc, \
952 "This module contains a PDF parser (based on xpdf) and a number of\n"
953 "rendering backends. In particular, it can extract text from PDF pages,\n"
954 "create bitmaps from them, or convert PDF files to SWF.\n" 
955 "The latter functionality is similar to what is offered by swftools'\n" 
956 "(http://www.swftools.org) pdf2swf utility, however more powerful-\n" 
957 "You can also create individual SWF files from single pages of the PDF\n" 
958 "or combine more than one page into a bigger PDF.\n"
959 );
960
961 void initgfx(void)
962 {
963     initLog(0,0,0,0,0,2);
964     OutputClass.ob_type = &PyType_Type;
965     PageClass.ob_type = &PyType_Type;
966     DocClass.ob_type = &PyType_Type;
967
968     pdfdriver = gfxsource_pdf_create();
969     swfdriver = gfxsource_swf_create();
970     imagedriver = gfxsource_image_create();
971     
972     PyObject*module = Py_InitModule3("gfx", pdf2swf_methods, gfx_doc);
973     PyObject*module_dict = PyModule_GetDict(module);
974
975     PyDict_SetItemString(module_dict, "Doc", (PyObject*)&DocClass);
976     PyDict_SetItemString(module_dict, "Page", (PyObject*)&PageClass);
977     PyDict_SetItemString(module_dict, "Output", (PyObject*)&OutputClass);
978 }