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