1 /* Python plug-in for dia
2  * Copyright (C) 1999  James Henstridge
3  * Copyright (C) 2000,2002  Hans Breuer
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 #include <config.h>
21 
22 #include <Python.h>
23 #include <glib.h>
24 #include <glib/gstdio.h>
25 
26 #include <locale.h>
27 
28 #include "intl.h"
29 #include "message.h"
30 #include "geometry.h"
31 
32 #include "pydia-object.h" /* for PyObject_HEAD_INIT */
33 #include "pydia-export.h"
34 #include "pydia-diagramdata.h"
35 #include "pydia-geometry.h"
36 #include "pydia-color.h"
37 #include "pydia-font.h"
38 #include "pydia-image.h"
39 #include "pydia-error.h"
40 
41 /*
42  * The PyDiaRenderer is currently defined in Python only. The
43  * DiaPyRenderer is using it's interface to map the gobject
44  * DiaRenderer to it. A next step could be to let Python code
45  * directly inherit from DiaPyRenderer.
46  * To do that probably some code from pygtk.gobject needs to
47  * be borrowed/shared
48  */
49 #include "diarenderer.h"
50 
51 #define DIA_TYPE_PY_RENDERER           (dia_py_renderer_get_type ())
52 #define DIA_PY_RENDERER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), DIA_TYPE_PY_RENDERER, DiaPyRenderer))
53 #define DIA_PY_RENDERER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), DIA_TYPE_PY_RENDERER, DiaPyRendererClass))
54 #define DIA_IS_PY_RENDERER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DIA_TYPE_PY_RENDERER))
55 #define DIA_PY_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DIA_TYPE_PY_RENDERER, DiaPyRendererClass))
56 
57 GType dia_py_renderer_get_type (void) G_GNUC_CONST;
58 
59 typedef struct _DiaPyRenderer DiaPyRenderer;
60 typedef struct _DiaPyRendererClass DiaPyRendererClass;
61 
62 struct _DiaPyRenderer
63 {
64   DiaRenderer parent_instance;
65 
66   char*     filename;
67   PyObject* self;
68   PyObject* diagram_data;
69   char*     old_locale;
70 };
71 
72 struct _DiaPyRendererClass
73 {
74   DiaRendererClass parent_class;
75 };
76 
77 #define PYDIA_RENDERER(renderer) \
78 	(DIA_PY_RENDERER(renderer)->self)
79 
80 /*
81  * Members overwritable by Python scripts
82  */
83 static void
begin_render(DiaRenderer * renderer)84 begin_render(DiaRenderer *renderer)
85 {
86   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
87 
88   DIA_PY_RENDERER(renderer)->old_locale = setlocale(LC_NUMERIC, "C");
89 
90   func = PyObject_GetAttrString (self, "begin_render");
91   if (func && PyCallable_Check(func)) {
92     Py_INCREF(self);
93     Py_INCREF(func);
94     arg = Py_BuildValue ("(Os)",
95                          DIA_PY_RENDERER(renderer)->diagram_data,
96                          DIA_PY_RENDERER(renderer)->filename);
97     if (arg) {
98       res = PyEval_CallObject (func, arg);
99       ON_RES(res, FALSE);
100     }
101     Py_XDECREF (arg);
102     Py_DECREF (func);
103     Py_DECREF (self);
104   }
105 }
106 
107 static void
end_render(DiaRenderer * renderer)108 end_render(DiaRenderer *renderer)
109 {
110   PyObject *func, *res, *self = PYDIA_RENDERER (renderer);
111 
112   func = PyObject_GetAttrString (self, "end_render");
113   if (func && PyCallable_Check(func)) {
114     Py_INCREF(self);
115     Py_INCREF(func);
116     res = PyEval_CallObject (func, (PyObject *)NULL);
117     ON_RES(res, FALSE);
118     Py_DECREF(func);
119     Py_DECREF(self);
120   }
121 
122   Py_DECREF (DIA_PY_RENDERER(renderer)->diagram_data);
123   g_free (DIA_PY_RENDERER(renderer)->filename);
124   DIA_PY_RENDERER(renderer)->filename = NULL;
125 
126   setlocale(LC_NUMERIC, DIA_PY_RENDERER(renderer)->old_locale);
127 }
128 
129 static void
set_linewidth(DiaRenderer * renderer,real linewidth)130 set_linewidth(DiaRenderer *renderer, real linewidth)
131 {
132   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
133 
134   func = PyObject_GetAttrString (self, "set_linewidth");
135   if (func && PyCallable_Check(func)) {
136     Py_INCREF(self);
137     Py_INCREF(func);
138     arg = Py_BuildValue ("(d)", linewidth);
139     if (arg) {
140       res = PyEval_CallObject (func, arg);
141       ON_RES(res, FALSE);
142     }
143     Py_XDECREF (arg);
144     Py_DECREF(func);
145     Py_DECREF(self);
146   }
147   else /* member optional */
148     PyErr_Clear();
149 }
150 
151 static void
set_linecaps(DiaRenderer * renderer,LineCaps mode)152 set_linecaps(DiaRenderer *renderer, LineCaps mode)
153 {
154   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
155 
156   switch(mode) {
157   case LINECAPS_BUTT:
158     break;
159   case LINECAPS_ROUND:
160     break;
161   case LINECAPS_PROJECTING:
162     break;
163   default:
164     PyErr_Warn (PyExc_RuntimeWarning, "DiaPyRenderer : Unsupported fill mode specified!\n");
165   }
166 
167   func = PyObject_GetAttrString (self, "set_linecaps");
168   if (func && PyCallable_Check(func)) {
169     Py_INCREF(self);
170     Py_INCREF(func);
171     arg = Py_BuildValue ("(i)", mode);
172     if (arg) {
173       res = PyEval_CallObject (func, arg);
174       ON_RES(res, FALSE);
175     }
176     Py_XDECREF (arg);
177     Py_DECREF(func);
178     Py_DECREF(self);
179   }
180   else /* member optional */
181     PyErr_Clear();
182 }
183 
184 static void
set_linejoin(DiaRenderer * renderer,LineJoin mode)185 set_linejoin(DiaRenderer *renderer, LineJoin mode)
186 {
187   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
188 
189   switch(mode) {
190     case LINEJOIN_MITER:
191     break;
192   case LINEJOIN_ROUND:
193     break;
194   case LINEJOIN_BEVEL:
195     break;
196   default:
197     PyErr_Warn (PyExc_RuntimeWarning, "DiaPyRenderer : Unsupported fill mode specified!\n");
198   }
199 
200   func = PyObject_GetAttrString (self, "set_linejoin");
201   if (func && PyCallable_Check(func)) {
202     Py_INCREF(self);
203     Py_INCREF(func);
204     arg = Py_BuildValue ("(i)", mode);
205     if (arg) {
206       res = PyEval_CallObject (func, arg);
207       ON_RES(res, FALSE);
208     }
209     Py_XDECREF (arg);
210     Py_DECREF(func);
211     Py_DECREF(self);
212   }
213   else /* member optional */
214     PyErr_Clear();
215 }
216 
217 static void
set_linestyle(DiaRenderer * renderer,LineStyle mode)218 set_linestyle(DiaRenderer *renderer, LineStyle mode)
219 {
220   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
221 
222   /* line type */
223   switch (mode) {
224   case LINESTYLE_SOLID:
225     break;
226   case LINESTYLE_DASHED:
227     break;
228   case LINESTYLE_DASH_DOT:
229     break;
230   case LINESTYLE_DASH_DOT_DOT:
231     break;
232   case LINESTYLE_DOTTED:
233     break;
234   default:
235     PyErr_Warn (PyExc_RuntimeWarning, "DiaPyRenderer : Unsupported fill mode specified!\n");
236   }
237 
238   func = PyObject_GetAttrString (self, "set_linestyle");
239   if (func && PyCallable_Check(func)) {
240     Py_INCREF(self);
241     Py_INCREF(func);
242     arg = Py_BuildValue ("(i)", mode);
243     if (arg) {
244       res = PyEval_CallObject (func, arg);
245       ON_RES(res, FALSE);
246     }
247     Py_XDECREF (arg);
248     Py_DECREF(func);
249     Py_DECREF(self);
250   }
251   else /* member optional */
252     PyErr_Clear();
253 }
254 
255 static void
set_dashlength(DiaRenderer * renderer,real length)256 set_dashlength(DiaRenderer *renderer, real length)
257 {
258   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
259 
260   func = PyObject_GetAttrString (self, "set_dashlength");
261   if (func && PyCallable_Check(func)) {
262     Py_INCREF(self);
263     Py_INCREF(func);
264     arg = Py_BuildValue ("(d)", length);
265     if (arg) {
266       res = PyEval_CallObject (func, arg);
267       ON_RES(res, FALSE);
268     }
269     Py_XDECREF (arg);
270     Py_DECREF(func);
271     Py_DECREF(self);
272   }
273   else /* member optional */
274     PyErr_Clear();
275 }
276 
277 static void
set_fillstyle(DiaRenderer * renderer,FillStyle mode)278 set_fillstyle(DiaRenderer *renderer, FillStyle mode)
279 {
280   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
281 
282   switch(mode) {
283   case FILLSTYLE_SOLID:
284     break;
285   default:
286     PyErr_Warn (PyExc_RuntimeWarning, "DiaPyRenderer : Unsupported fill mode specified!\n");
287   }
288 
289   func = PyObject_GetAttrString (self, "set_fillstyle");
290   if (func && PyCallable_Check(func)) {
291     Py_INCREF(self);
292     Py_INCREF(func);
293     arg = Py_BuildValue ("(i)", mode);
294     if (arg) {
295       res = PyEval_CallObject (func, arg);
296       ON_RES(res, FALSE);
297     }
298     Py_XDECREF (arg);
299     Py_DECREF(func);
300     Py_DECREF(self);
301   }
302   else /* member optional */
303     PyErr_Clear();
304 }
305 
306 static void
set_font(DiaRenderer * renderer,DiaFont * font,real height)307 set_font(DiaRenderer *renderer, DiaFont *font, real height)
308 {
309   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
310 
311   func = PyObject_GetAttrString (self, "set_font");
312   if (func && PyCallable_Check(func)) {
313     Py_INCREF(self);
314     Py_INCREF(func);
315     arg = Py_BuildValue ("(Od)", PyDiaFont_New (font), height);
316     if (arg) {
317       res = PyEval_CallObject (func, arg);
318       ON_RES(res, FALSE);
319     }
320     Py_XDECREF (arg);
321     Py_DECREF(func);
322     Py_DECREF(self);
323   }
324   else /* member optional */
325     PyErr_Clear();
326 }
327 
328 static gpointer parent_class = NULL;
329 
330 static void
draw_line(DiaRenderer * renderer,Point * start,Point * end,Color * line_colour)331 draw_line(DiaRenderer *renderer,
332           Point *start, Point *end,
333           Color *line_colour)
334 {
335   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
336 
337   func = PyObject_GetAttrString (self, "draw_line");
338   if (func && PyCallable_Check(func)) {
339     Py_INCREF(self);
340     Py_INCREF(func);
341     arg = Py_BuildValue ("(OOO)", PyDiaPoint_New (start),
342                                   PyDiaPoint_New (end),
343                                   PyDiaColor_New (line_colour));
344     if (arg) {
345       res = PyEval_CallObject (func, arg);
346       ON_RES(res, FALSE);
347     }
348     Py_XDECREF (arg);
349     Py_DECREF(func);
350     Py_DECREF(self);
351   }
352   else { /* member not optional */
353     gchar *msg = g_strdup_printf ("%s.draw_line() implmentation missing.",
354 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
355     PyErr_Clear();
356     PyErr_Warn (PyExc_RuntimeWarning, msg);
357     g_free (msg);
358   }
359 }
360 
361 static void
draw_polyline(DiaRenderer * renderer,Point * points,int num_points,Color * line_colour)362 draw_polyline(DiaRenderer *renderer,
363 	      Point *points, int num_points,
364 	      Color *line_colour)
365 {
366   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
367 
368   func = PyObject_GetAttrString (self, "draw_polyline");
369   if (func && PyCallable_Check(func)) {
370     Py_INCREF(self);
371     Py_INCREF(func);
372     arg = Py_BuildValue ("(OO)", PyDiaPointTuple_New (points, num_points),
373                                  PyDiaColor_New (line_colour));
374     if (arg) {
375       res = PyEval_CallObject (func, arg);
376       ON_RES(res, FALSE);
377     }
378     Py_XDECREF (arg);
379     Py_DECREF(func);
380     Py_DECREF(self);
381   }
382   else { /* member optional */
383     PyErr_Clear();
384     /* XXX: implementing the same fallback as DiaRenderer */
385     DIA_RENDERER_CLASS (parent_class)->draw_polyline (renderer, points, num_points, line_colour);
386   }
387 }
388 
389 static void
draw_polygon(DiaRenderer * renderer,Point * points,int num_points,Color * line_colour)390 draw_polygon(DiaRenderer *renderer,
391 	     Point *points, int num_points,
392 	     Color *line_colour)
393 {
394   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
395 
396   func = PyObject_GetAttrString (self, "draw_polygon");
397   if (func && PyCallable_Check(func)) {
398     Py_INCREF(self);
399     Py_INCREF(func);
400     arg = Py_BuildValue ("(OO)", PyDiaPointTuple_New (points, num_points),
401                                  PyDiaColor_New (line_colour));
402     if (arg) {
403       res = PyEval_CallObject (func, arg);
404       ON_RES(res, FALSE);
405     }
406     Py_XDECREF (arg);
407     Py_DECREF(func);
408     Py_DECREF(self);
409   }
410   else { /* member optional */
411     PyErr_Clear();
412     /* XXX: implementing the same fallback as DiaRenderer would do */
413     DIA_RENDERER_CLASS (parent_class)->draw_polygon (renderer, points, num_points, line_colour);
414   }
415 }
416 
417 static void
fill_polygon(DiaRenderer * renderer,Point * points,int num_points,Color * colour)418 fill_polygon(DiaRenderer *renderer,
419 	     Point *points, int num_points,
420 	     Color *colour)
421 {
422   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
423 
424   func = PyObject_GetAttrString (self, "fill_polygon");
425   if (func && PyCallable_Check(func)) {
426     Py_INCREF(self);
427     Py_INCREF(func);
428     arg = Py_BuildValue ("(OO)", PyDiaPointTuple_New (points, num_points),
429                                  PyDiaColor_New (colour));
430     if (arg) {
431       res = PyEval_CallObject (func, arg);
432       ON_RES(res, FALSE);
433     }
434     Py_XDECREF (arg);
435     Py_DECREF(func);
436     Py_DECREF(self);
437   }
438   else { /* member not optional */
439     gchar *msg = g_strdup_printf ("%s.fill_polygon() implmentation missing.",
440 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
441     PyErr_Clear();
442     PyErr_Warn (PyExc_RuntimeWarning, msg);
443     g_free (msg);
444   }
445 }
446 
447 static void
draw_rect(DiaRenderer * renderer,Point * ul_corner,Point * lr_corner,Color * colour)448 draw_rect(DiaRenderer *renderer,
449 	  Point *ul_corner, Point *lr_corner,
450 	  Color *colour)
451 {
452   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
453 
454   func = PyObject_GetAttrString (self, "draw_rect");
455   if (func && PyCallable_Check(func)) {
456     Py_INCREF(self);
457     Py_INCREF(func);
458     arg = Py_BuildValue ("(OO)", PyDiaRectangle_New_FromPoints (ul_corner, lr_corner),
459                                  PyDiaColor_New (colour));
460     if (arg) {
461       res = PyEval_CallObject (func, arg);
462       ON_RES(res, FALSE);
463     }
464     Py_XDECREF (arg);
465     Py_DECREF(func);
466     Py_DECREF(self);
467   }
468   else { /* member optional */
469     PyErr_Clear();
470     /* XXX: implementing the same fallback as DiaRenderer would do */
471     DIA_RENDERER_CLASS (parent_class)->draw_rect (renderer, ul_corner, lr_corner, colour);
472   }
473 }
474 
475 static void
fill_rect(DiaRenderer * renderer,Point * ul_corner,Point * lr_corner,Color * colour)476 fill_rect(DiaRenderer *renderer,
477 	  Point *ul_corner, Point *lr_corner,
478 	  Color *colour)
479 {
480   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
481 
482   func = PyObject_GetAttrString (self, "fill_rect");
483   if (func && PyCallable_Check(func)) {
484     Py_INCREF(self);
485     Py_INCREF(func);
486     arg = Py_BuildValue ("(OO)", PyDiaRectangle_New_FromPoints (ul_corner, lr_corner),
487                                  PyDiaColor_New (colour));
488     if (arg) {
489       res = PyEval_CallObject (func, arg);
490       ON_RES(res, FALSE);
491     }
492     Py_XDECREF (arg);
493     Py_DECREF(func);
494     Py_DECREF(self);
495   }
496   else { /* member not optional */
497     gchar *msg = g_strdup_printf ("%s.fill_rect() implmentation missing.",
498 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
499     PyErr_Clear();
500     PyErr_Warn (PyExc_RuntimeWarning, msg);
501     g_free (msg);
502   }
503 }
504 
505 static void
draw_arc(DiaRenderer * renderer,Point * center,real width,real height,real angle1,real angle2,Color * colour)506 draw_arc(DiaRenderer *renderer,
507 	 Point *center,
508 	 real width, real height,
509 	 real angle1, real angle2,
510 	 Color *colour)
511 {
512   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
513 
514   func = PyObject_GetAttrString (self, "draw_arc");
515   if (func && PyCallable_Check(func)) {
516     Py_INCREF(self);
517     Py_INCREF(func);
518     arg = Py_BuildValue ("(OddddO)", PyDiaPoint_New (center),
519                                      width, height, angle1, angle2,
520                                      PyDiaColor_New (colour));
521     if (arg) {
522       res = PyEval_CallObject (func, arg);
523       ON_RES(res, FALSE);
524     }
525     Py_XDECREF (arg);
526     Py_DECREF(func);
527     Py_DECREF(self);
528   }
529   else { /* member not optional */
530     gchar *msg = g_strdup_printf ("%s.draw_arc() implmentation missing.",
531 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
532     PyErr_Clear();
533     PyErr_Warn (PyExc_RuntimeWarning, msg);
534     g_free (msg);
535   }
536 }
537 
538 static void
fill_arc(DiaRenderer * renderer,Point * center,real width,real height,real angle1,real angle2,Color * colour)539 fill_arc(DiaRenderer *renderer,
540 	 Point *center,
541 	 real width, real height,
542 	 real angle1, real angle2,
543 	 Color *colour)
544 {
545   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
546 
547   func = PyObject_GetAttrString (self, "fill_arc");
548   if (func && PyCallable_Check(func)) {
549     Py_INCREF(self);
550     Py_INCREF(func);
551     arg = Py_BuildValue ("(OddddO)", PyDiaPoint_New (center),
552                                      width, height, angle1, angle2,
553                                      PyDiaColor_New (colour));
554     if (arg) {
555       res = PyEval_CallObject (func, arg);
556       ON_RES(res, FALSE);
557     }
558     Py_XDECREF (arg);
559     Py_DECREF(func);
560     Py_DECREF(self);
561   }
562   else { /* member not optional */
563     gchar *msg = g_strdup_printf ("%s.fill_arc() implmentation missing.",
564 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
565     PyErr_Clear();
566     PyErr_Warn (PyExc_RuntimeWarning, msg);
567     g_free (msg);
568   }
569 }
570 
571 static void
draw_ellipse(DiaRenderer * renderer,Point * center,real width,real height,Color * colour)572 draw_ellipse(DiaRenderer *renderer,
573 	     Point *center,
574 	     real width, real height,
575 	     Color *colour)
576 {
577   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
578 
579   func = PyObject_GetAttrString (self, "draw_ellipse");
580   if (func && PyCallable_Check(func)) {
581     Py_INCREF(self);
582     Py_INCREF(func);
583     arg = Py_BuildValue ("(OddO)", PyDiaPoint_New (center),
584                                    width, height,
585                                    PyDiaColor_New (colour));
586     if (arg) {
587       res = PyEval_CallObject (func, arg);
588       ON_RES(res, FALSE);
589     }
590     Py_XDECREF (arg);
591     Py_DECREF(func);
592     Py_DECREF(self);
593   }
594   else { /* member not optional */
595     gchar *msg = g_strdup_printf ("%s.draw_ellipse() implmentation missing.",
596 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
597     PyErr_Clear();
598     PyErr_Warn (PyExc_RuntimeWarning, msg);
599     g_free (msg);
600   }
601 }
602 
603 static void
fill_ellipse(DiaRenderer * renderer,Point * center,real width,real height,Color * colour)604 fill_ellipse(DiaRenderer *renderer,
605 	     Point *center,
606 	     real width, real height,
607 	     Color *colour)
608 {
609   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
610 
611   func = PyObject_GetAttrString (self, "fill_ellipse");
612   if (func && PyCallable_Check(func)) {
613     Py_INCREF(self);
614     Py_INCREF(func);
615     arg = Py_BuildValue ("(OddO)", PyDiaPoint_New (center),
616                                    width, height,
617                                    PyDiaColor_New (colour));
618     if (arg) {
619       res = PyEval_CallObject (func, arg);
620       ON_RES(res, FALSE);
621     }
622     Py_XDECREF (arg);
623     Py_DECREF(func);
624     Py_DECREF(self);
625   }
626   else { /* member not optional */
627     gchar *msg = g_strdup_printf ("%s.fill_ellipse() implmentation missing.",
628 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
629     PyErr_Clear();
630     PyErr_Warn (PyExc_RuntimeWarning, msg);
631     g_free (msg);
632   }
633 }
634 
635 static void
draw_bezier(DiaRenderer * renderer,BezPoint * points,int num_points,Color * colour)636 draw_bezier(DiaRenderer *renderer,
637 	    BezPoint *points,
638 	    int num_points,
639 	    Color *colour)
640 {
641   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
642 
643   func = PyObject_GetAttrString (self, "draw_bezier");
644   if (func && PyCallable_Check(func)) {
645     Py_INCREF(self);
646     Py_INCREF(func);
647     arg = Py_BuildValue ("(OO)", PyDiaBezPointTuple_New (points, num_points),
648                                  PyDiaColor_New (colour));
649     if (arg) {
650       res = PyEval_CallObject (func, arg);
651       ON_RES(res, FALSE);
652     }
653     Py_XDECREF (arg);
654     Py_DECREF(func);
655     Py_DECREF(self);
656   }
657   else { /* member optional */
658     PyErr_Clear();
659     /* XXX: implementing the same fallback as DiaRenderer would do */
660     DIA_RENDERER_CLASS (parent_class)->draw_bezier (renderer, points, num_points, colour);
661   }
662 }
663 
664 static void
fill_bezier(DiaRenderer * renderer,BezPoint * points,int num_points,Color * colour)665 fill_bezier(DiaRenderer *renderer,
666 	    BezPoint *points, /* Last point must be same as first point */
667 	    int num_points,
668 	    Color *colour)
669 {
670   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
671 
672   func = PyObject_GetAttrString (self, "fill_bezier");
673   if (func && PyCallable_Check(func)) {
674     Py_INCREF(self);
675     Py_INCREF(func);
676     arg = Py_BuildValue ("(OO)", PyDiaBezPointTuple_New (points, num_points),
677                                  PyDiaColor_New (colour));
678     if (arg) {
679       res = PyEval_CallObject (func, arg);
680       ON_RES(res, FALSE);
681     }
682     Py_XDECREF (arg);
683     Py_DECREF(func);
684     Py_DECREF(self);
685   }
686   else { /* member optional */
687     PyErr_Clear();
688     /* XXX: implementing the same fallback as DiaRenderer would do */
689     DIA_RENDERER_CLASS (parent_class)->fill_bezier (renderer, points, num_points, colour);
690   }
691 }
692 
693 static void
draw_string(DiaRenderer * renderer,const char * text,Point * pos,Alignment alignment,Color * colour)694 draw_string(DiaRenderer *renderer,
695 	    const char *text,
696 	    Point *pos, Alignment alignment,
697 	    Color *colour)
698 {
699   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
700   int len;
701 
702   switch (alignment) {
703   case ALIGN_LEFT:
704     break;
705   case ALIGN_CENTER:
706     break;
707   case ALIGN_RIGHT:
708     break;
709   }
710   /* work out size of first chunk of text */
711   len = strlen(text);
712 
713   func = PyObject_GetAttrString (self, "draw_string");
714   if (func && PyCallable_Check(func)) {
715     Py_INCREF(self);
716     Py_INCREF(func);
717     arg = Py_BuildValue ("(sOiO)", text,
718                                    PyDiaPoint_New (pos),
719                                    alignment,
720                                    PyDiaColor_New (colour));
721     if (arg) {
722       res = PyEval_CallObject (func, arg);
723       ON_RES(res, FALSE);
724     }
725     Py_XDECREF (arg);
726     Py_DECREF(func);
727     Py_DECREF(self);
728   } else { /* member not optional */
729     gchar *msg = g_strdup_printf ("%s.draw_string() implmentation missing.",
730 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
731     PyErr_Clear();
732     PyErr_Warn (PyExc_RuntimeWarning, msg);
733     g_free (msg);
734   }
735 }
736 
737 static void
draw_image(DiaRenderer * renderer,Point * point,real width,real height,DiaImage * image)738 draw_image(DiaRenderer *renderer,
739 	   Point *point,
740 	   real width, real height,
741 	   DiaImage *image)
742 {
743   PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
744 
745   func = PyObject_GetAttrString (self, "draw_image");
746   if (func && PyCallable_Check(func)) {
747     Py_INCREF(self);
748     Py_INCREF(func);
749     arg = Py_BuildValue ("(OddO)", PyDiaPoint_New (point),
750                                    width, height,
751                                    PyDiaImage_New (image));
752     if (arg) {
753       res = PyEval_CallObject (func, arg);
754       ON_RES(res, FALSE);
755     }
756     Py_XDECREF (arg);
757     Py_DECREF(func);
758     Py_DECREF(self);
759   } else { /* member not optional */
760     gchar *msg = g_strdup_printf ("%s.draw_string() implmentation missing.",
761 				  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
762     PyErr_Clear();
763     PyErr_Warn (PyExc_RuntimeWarning, msg);
764     g_free (msg);
765   }
766 }
767 
768 void
PyDia_export_data(DiagramData * data,const gchar * filename,const gchar * diafilename,void * user_data)769 PyDia_export_data(DiagramData *data, const gchar *filename,
770                   const gchar *diafilename, void* user_data)
771 {
772   DiaPyRenderer *renderer;
773 
774   {
775     FILE *file;
776     file = g_fopen(filename, "w"); /* "wb" for binary! */
777 
778     if (file == NULL) {
779       message_error(_("Couldn't open '%s' for writing.\n"),
780 		    dia_message_filename(filename));
781       return;
782     }
783     else
784       fclose (file);
785   }
786 
787   renderer = g_object_new (DIA_TYPE_PY_RENDERER, NULL);
788 
789   renderer->filename = g_strdup (filename);
790   renderer->diagram_data = PyDiaDiagramData_New(data);
791 
792   /* The Python Renderer object was created at PyDia_Register */
793   renderer->self = (PyObject*)user_data;
794 
795   /* this will call the required callback functions above */
796   data_render(data, DIA_RENDERER(renderer), NULL, NULL, NULL);
797 
798   g_object_unref(renderer);
799 }
800 
801 /*
802  * GObject boiler plate
803  */
804 static void dia_py_renderer_class_init (DiaPyRendererClass *klass);
805 
806 GType
dia_py_renderer_get_type(void)807 dia_py_renderer_get_type (void)
808 {
809   static GType object_type = 0;
810 
811   if (!object_type)
812     {
813       static const GTypeInfo object_info =
814       {
815         sizeof (DiaPyRendererClass),
816         (GBaseInitFunc) NULL,
817         (GBaseFinalizeFunc) NULL,
818         (GClassInitFunc) dia_py_renderer_class_init,
819         NULL,           /* class_finalize */
820         NULL,           /* class_data */
821         sizeof (DiaPyRenderer),
822         0,              /* n_preallocs */
823 	NULL            /* init */
824       };
825 
826       object_type = g_type_register_static (DIA_TYPE_RENDERER,
827                                             "DiaPyRenderer",
828                                             &object_info, 0);
829     }
830 
831   return object_type;
832 }
833 
834 static void
dia_py_renderer_finalize(GObject * object)835 dia_py_renderer_finalize (GObject *object)
836 {
837   DiaPyRenderer *renderer = DIA_PY_RENDERER (object);
838 
839   if (renderer->filename)
840     g_free (renderer->filename);
841 
842   G_OBJECT_CLASS (parent_class)->finalize (object);
843 }
844 
845 static void
dia_py_renderer_class_init(DiaPyRendererClass * klass)846 dia_py_renderer_class_init (DiaPyRendererClass *klass)
847 {
848   GObjectClass *object_class = G_OBJECT_CLASS (klass);
849   DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
850 
851   parent_class = g_type_class_peek_parent (klass);
852 
853   object_class->finalize = dia_py_renderer_finalize;
854 
855   /* all defined members from above */
856   /* renderer members */
857   renderer_class->begin_render = begin_render;
858   renderer_class->end_render   = end_render;
859 
860   renderer_class->set_linewidth  = set_linewidth;
861   renderer_class->set_linecaps   = set_linecaps;
862   renderer_class->set_linejoin   = set_linejoin;
863   renderer_class->set_linestyle  = set_linestyle;
864   renderer_class->set_dashlength = set_dashlength;
865   renderer_class->set_fillstyle  = set_fillstyle;
866 
867   renderer_class->set_font  = set_font;
868 
869   renderer_class->draw_line    = draw_line;
870   renderer_class->fill_polygon = fill_polygon;
871   renderer_class->draw_rect    = draw_rect;
872   renderer_class->fill_rect    = fill_rect;
873   renderer_class->draw_arc     = draw_arc;
874   renderer_class->fill_arc     = fill_arc;
875   renderer_class->draw_ellipse = draw_ellipse;
876   renderer_class->fill_ellipse = fill_ellipse;
877 
878   renderer_class->draw_string  = draw_string;
879   renderer_class->draw_image   = draw_image;
880 
881   /* medium level functions */
882   renderer_class->draw_rect = draw_rect;
883   renderer_class->draw_polyline  = draw_polyline;
884   renderer_class->draw_polygon   = draw_polygon;
885 
886   renderer_class->draw_bezier   = draw_bezier;
887   renderer_class->fill_bezier   = fill_bezier;
888 }
889 
890