Okay, here we are, sooner than I expected =)
I attached two files to this post and hopefully, I did not edit any
other that I forgot to attach.
In pydia-render.c, I added draw_rounded_rect and fill_rounded_rect and
declared them at the bottom of the file. I needed that to get pretty
clean SVG code that's rendered correctly in Firefox. Before that, other
functions like ellipse or arc were used to achieve this and that ended
up in strange results.
The other file I added is my diasvgcss.py file. It's based on Hans'
diasvg.py with the following modifications:
- scale all coordinates and line widths with 20 (as in the C renderer)
- corrected SVG viewBox (width must be width-x, height must be height-x,
that thing made me nearly crazy *g*)
- put all objects in group tags
- added meta id as group class
- added object name as group name
- if an item has a meta id, the fill or stroke value is saved in a
separate CSS file (<svgname>.css), if not it's saved in the tag itself
I hope that my modifications are useful to someone else than me so that
they find their way to the default Dia version. Please be kind if there
are any C or Python mistakes, I did not have much contact to either
before that ;-)
As I mentioned in an earlier post, I'm using this to extend Concordion
tests with diagrams. I'm about to write a blog entry for our company's
blog and I'll post the link to it once it's finished.
If anyone's interested in, I could also merge my modifications to the
original diasvg.py, except the export of the CSS attributes. I'm always
interested in feedback, so if you've got any questions or suggestions,
feel free to drop me a line. Let me know what you think about it!
Regards
Max
Am 25.01.11 21:27, schrieb Max Wahler:
Hi CGodefroy,
as Steffen already mentioned, I'm currently also working on customizing
the Python SVG exporter. Actually there _is_ a draw_object method! It's
in line 50 of the current GIT version. I hope that I'm ready to commit
my code for review to this mailing list this week.
A few words about what I'm doing: It groups the tags for objects and
sets the object's id as class argument. I use this to style the objects
afterwards via CSS.
Regards
Max
Am 25.01.2011 15:06, schrieb Cgodefroy:
Hello,
I would like to customize the Dia SVG export. I need to insert into my
SVG
document, informations about the objets (like their id). I have found the
diasvg.py file which seems to be the simplest way to customize my export.
Unfortunately, in this file there is no "draw_object" function. Do you
know
if there is a simple way to do my export? Has some people insert the
draw_object function in the diasvg.py file ?
Thanks for your help.
Regards
CGodefroy
_______________________________________________
dia-list mailing list
dia-list@gnome.org
http://mail.gnome.org/mailman/listinfo/dia-list
FAQ at http://live.gnome.org/Dia/Faq
Main page at http://live.gnome.org/Dia
--
All those, who believe in telekinesis, raise my hand.
/* Python plug-in for dia
* Copyright (C) 1999 James Henstridge
* Copyright (C) 2000,2002 Hans Breuer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <config.h>
#include <Python.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <locale.h>
#include "intl.h"
#include "message.h"
#include "geometry.h"
#include "pydia-object.h" /* for PyObject_HEAD_INIT */
#include "pydia-export.h"
#include "pydia-diagramdata.h"
#include "pydia-geometry.h"
#include "pydia-color.h"
#include "pydia-font.h"
#include "pydia-image.h"
#include "pydia-error.h"
#include "pydia-render.h"
/*
* The PyDiaRenderer is currently defined in Python only. The
* DiaPyRenderer is using it's interface to map the gobject
* DiaRenderer to it. A next step could be to let Python code
* directly inherit from DiaPyRenderer.
* To do that probably some code from pygtk.gobject needs to
* be borrowed/shared
*/
#include "diarenderer.h"
#define DIA_TYPE_PY_RENDERER (dia_py_renderer_get_type ())
#define DIA_PY_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
DIA_TYPE_PY_RENDERER, DiaPyRenderer))
#define DIA_PY_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
DIA_TYPE_PY_RENDERER, DiaPyRendererClass))
#define DIA_IS_PY_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
DIA_TYPE_PY_RENDERER))
#define DIA_PY_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
DIA_TYPE_PY_RENDERER, DiaPyRendererClass))
GType dia_py_renderer_get_type (void) G_GNUC_CONST;
typedef struct _DiaPyRenderer DiaPyRenderer;
typedef struct _DiaPyRendererClass DiaPyRendererClass;
struct _DiaPyRenderer
{
DiaRenderer parent_instance;
char* filename;
PyObject* self;
PyObject* diagram_data;
char* old_locale;
};
struct _DiaPyRendererClass
{
DiaRendererClass parent_class;
};
#define PYDIA_RENDERER(renderer) \
(DIA_PY_RENDERER(renderer)->self)
/*
* Members overwritable by Python scripts
*/
static void
begin_render(DiaRenderer *renderer)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
DIA_PY_RENDERER(renderer)->old_locale = setlocale(LC_NUMERIC, "C");
func = PyObject_GetAttrString (self, "begin_render");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(Os)",
DIA_PY_RENDERER(renderer)->diagram_data,
DIA_PY_RENDERER(renderer)->filename);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF (func);
Py_DECREF (self);
}
}
static void
end_render(DiaRenderer *renderer)
{
PyObject *func, *res, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "end_render");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
res = PyEval_CallObject (func, (PyObject *)NULL);
ON_RES(res, FALSE);
Py_DECREF(func);
Py_DECREF(self);
}
Py_DECREF (DIA_PY_RENDERER(renderer)->diagram_data);
g_free (DIA_PY_RENDERER(renderer)->filename);
DIA_PY_RENDERER(renderer)->filename = NULL;
setlocale(LC_NUMERIC, DIA_PY_RENDERER(renderer)->old_locale);
}
static void
set_linewidth(DiaRenderer *renderer, real linewidth)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "set_linewidth");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(d)", linewidth);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else /* member optional */
PyErr_Clear();
}
static void
set_linecaps(DiaRenderer *renderer, LineCaps mode)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
switch(mode) {
case LINECAPS_BUTT:
break;
case LINECAPS_ROUND:
break;
case LINECAPS_PROJECTING:
break;
default:
PyErr_Warn (PyExc_RuntimeWarning, "DiaPyRenderer : Unsupported fill mode
specified!\n");
}
func = PyObject_GetAttrString (self, "set_linecaps");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(i)", mode);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else /* member optional */
PyErr_Clear();
}
static void
set_linejoin(DiaRenderer *renderer, LineJoin mode)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
switch(mode) {
case LINEJOIN_MITER:
break;
case LINEJOIN_ROUND:
break;
case LINEJOIN_BEVEL:
break;
default:
PyErr_Warn (PyExc_RuntimeWarning, "DiaPyRenderer : Unsupported fill mode
specified!\n");
}
func = PyObject_GetAttrString (self, "set_linejoin");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(i)", mode);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else /* member optional */
PyErr_Clear();
}
static void
set_linestyle(DiaRenderer *renderer, LineStyle mode)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
/* line type */
switch (mode) {
case LINESTYLE_SOLID:
break;
case LINESTYLE_DASHED:
break;
case LINESTYLE_DASH_DOT:
break;
case LINESTYLE_DASH_DOT_DOT:
break;
case LINESTYLE_DOTTED:
break;
default:
PyErr_Warn (PyExc_RuntimeWarning, "DiaPyRenderer : Unsupported fill mode
specified!\n");
}
func = PyObject_GetAttrString (self, "set_linestyle");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(i)", mode);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else /* member optional */
PyErr_Clear();
}
static void
set_dashlength(DiaRenderer *renderer, real length)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "set_dashlength");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(d)", length);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else /* member optional */
PyErr_Clear();
}
static void
set_fillstyle(DiaRenderer *renderer, FillStyle mode)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
switch(mode) {
case FILLSTYLE_SOLID:
break;
default:
PyErr_Warn (PyExc_RuntimeWarning, "DiaPyRenderer : Unsupported fill mode
specified!\n");
}
func = PyObject_GetAttrString (self, "set_fillstyle");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(i)", mode);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else /* member optional */
PyErr_Clear();
}
static void
set_font(DiaRenderer *renderer, DiaFont *font, real height)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "set_font");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(Od)", PyDiaFont_New (font), height);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else /* member optional */
PyErr_Clear();
}
static gpointer parent_class = NULL;
static void
draw_object (DiaRenderer *renderer, DiaObject *object, DiaMatrix *matrix)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_object");
if (func && PyCallable_Check(func)) {
PyObject *mat = NULL;
Py_INCREF(self);
Py_INCREF(func);
if (matrix)
mat = PyDiaMatrix_New (matrix);
else {
Py_INCREF(Py_None);
mat = Py_None;
}
arg = Py_BuildValue ("(OO)", PyDiaObject_New (object), mat );
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_XDECREF (mat);
Py_DECREF(func);
Py_DECREF(self);
} else { /* member optional */
PyErr_Clear();
/* but should still call the base class */
DIA_RENDERER_CLASS (parent_class)->draw_object (renderer, object, matrix);
}
}
static void
draw_line(DiaRenderer *renderer,
Point *start, Point *end,
Color *line_colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_line");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OOO)", PyDiaPoint_New (start),
PyDiaPoint_New (end),
PyDiaColor_New (line_colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.draw_line() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
static void
draw_polyline(DiaRenderer *renderer,
Point *points, int num_points,
Color *line_colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_polyline");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OO)", PyDiaPointTuple_New (points, num_points),
PyDiaColor_New (line_colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member optional */
PyErr_Clear();
/* XXX: implementing the same fallback as DiaRenderer */
DIA_RENDERER_CLASS (parent_class)->draw_polyline (renderer, points,
num_points, line_colour);
}
}
static void
draw_polygon(DiaRenderer *renderer,
Point *points, int num_points,
Color *line_colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_polygon");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OO)", PyDiaPointTuple_New (points, num_points),
PyDiaColor_New (line_colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member optional */
PyErr_Clear();
/* XXX: implementing the same fallback as DiaRenderer would do */
DIA_RENDERER_CLASS (parent_class)->draw_polygon (renderer, points,
num_points, line_colour);
}
}
static void
fill_polygon(DiaRenderer *renderer,
Point *points, int num_points,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "fill_polygon");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OO)", PyDiaPointTuple_New (points, num_points),
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.fill_polygon() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
static void
draw_rect(DiaRenderer *renderer,
Point *ul_corner, Point *lr_corner,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_rect");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OO)", PyDiaRectangle_New_FromPoints (ul_corner,
lr_corner),
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member optional */
PyErr_Clear();
/* XXX: implementing the same fallback as DiaRenderer would do */
DIA_RENDERER_CLASS (parent_class)->draw_rect (renderer, ul_corner,
lr_corner, colour);
}
}
static void
draw_rounded_rect(DiaRenderer *renderer,
Point *ul_corner, Point *lr_corner,
Color *colour, real rounding)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_rounded_rect");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OOd)", PyDiaRectangle_New_FromPoints (ul_corner,
lr_corner),
PyDiaColor_New (colour), rounding);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member optional */
PyErr_Clear();
/* XXX: implementing the same fallback as DiaRenderer would do */
DIA_RENDERER_CLASS (parent_class)->draw_rounded_rect (renderer, ul_corner,
lr_corner, colour, rounding);
}
}
static void
fill_rect(DiaRenderer *renderer,
Point *ul_corner, Point *lr_corner,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "fill_rect");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OO)", PyDiaRectangle_New_FromPoints (ul_corner,
lr_corner),
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.fill_rect() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
static void
fill_rounded_rect(DiaRenderer *renderer,
Point *ul_corner, Point *lr_corner,
Color *colour, real rounding)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "fill_rounded_rect");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OOd)", PyDiaRectangle_New_FromPoints (ul_corner,
lr_corner),
PyDiaColor_New (colour), rounding);
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member optional */
PyErr_Clear();
/* XXX: implementing the same fallback as DiaRenderer would do */
DIA_RENDERER_CLASS (parent_class)->fill_rounded_rect (renderer, ul_corner,
lr_corner, colour, rounding);
}
}
static void
draw_arc(DiaRenderer *renderer,
Point *center,
real width, real height,
real angle1, real angle2,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_arc");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OddddO)", PyDiaPoint_New (center),
width, height, angle1, angle2,
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.draw_arc() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
static void
fill_arc(DiaRenderer *renderer,
Point *center,
real width, real height,
real angle1, real angle2,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "fill_arc");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OddddO)", PyDiaPoint_New (center),
width, height, angle1, angle2,
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.fill_arc() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
static void
draw_ellipse(DiaRenderer *renderer,
Point *center,
real width, real height,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_ellipse");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OddO)", PyDiaPoint_New (center),
width, height,
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.draw_ellipse() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
static void
fill_ellipse(DiaRenderer *renderer,
Point *center,
real width, real height,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "fill_ellipse");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OddO)", PyDiaPoint_New (center),
width, height,
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.fill_ellipse() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
static void
draw_bezier(DiaRenderer *renderer,
BezPoint *points,
int num_points,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_bezier");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OO)", PyDiaBezPointTuple_New (points, num_points),
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member optional */
PyErr_Clear();
/* XXX: implementing the same fallback as DiaRenderer would do */
DIA_RENDERER_CLASS (parent_class)->draw_bezier (renderer, points,
num_points, colour);
}
}
static void
fill_bezier(DiaRenderer *renderer,
BezPoint *points, /* Last point must be same as first point */
int num_points,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "fill_bezier");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OO)", PyDiaBezPointTuple_New (points, num_points),
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
}
else { /* member optional */
PyErr_Clear();
/* XXX: implementing the same fallback as DiaRenderer would do */
DIA_RENDERER_CLASS (parent_class)->fill_bezier (renderer, points,
num_points, colour);
}
}
static void
draw_string(DiaRenderer *renderer,
const char *text,
Point *pos, Alignment alignment,
Color *colour)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
int len;
switch (alignment) {
case ALIGN_LEFT:
break;
case ALIGN_CENTER:
break;
case ALIGN_RIGHT:
break;
}
/* work out size of first chunk of text */
len = strlen(text);
func = PyObject_GetAttrString (self, "draw_string");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(sOiO)", text,
PyDiaPoint_New (pos),
alignment,
PyDiaColor_New (colour));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
} else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.draw_string() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
static void
draw_image(DiaRenderer *renderer,
Point *point,
real width, real height,
DiaImage *image)
{
PyObject *func, *res, *arg, *self = PYDIA_RENDERER (renderer);
func = PyObject_GetAttrString (self, "draw_image");
if (func && PyCallable_Check(func)) {
Py_INCREF(self);
Py_INCREF(func);
arg = Py_BuildValue ("(OddO)", PyDiaPoint_New (point),
width, height,
PyDiaImage_New (image));
if (arg) {
res = PyEval_CallObject (func, arg);
ON_RES(res, FALSE);
}
Py_XDECREF (arg);
Py_DECREF(func);
Py_DECREF(self);
} else { /* member not optional */
gchar *msg = g_strdup_printf ("%s.draw_string() implmentation missing.",
G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS
(renderer)));
PyErr_Clear();
PyErr_Warn (PyExc_RuntimeWarning, msg);
g_free (msg);
}
}
void
PyDia_export_data(DiagramData *data, const gchar *filename,
const gchar *diafilename, void* user_data)
{
DiaPyRenderer *renderer;
{
FILE *file;
file = g_fopen(filename, "w"); /* "wb" for binary! */
if (file == NULL) {
message_error(_("Couldn't open '%s' for writing.\n"),
dia_message_filename(filename));
return;
}
else
fclose (file);
}
renderer = g_object_new (DIA_TYPE_PY_RENDERER, NULL);
renderer->filename = g_strdup (filename);
renderer->diagram_data = PyDiaDiagramData_New(data);
/* The Python Renderer object was created at PyDia_Register */
renderer->self = (PyObject*)user_data;
/* this will call the required callback functions above */
data_render(data, DIA_RENDERER(renderer), NULL, NULL, NULL);
g_object_unref(renderer);
}
DiaRenderer *
PyDia_new_renderer_wrapper (PyObject *self)
{
DiaPyRenderer *wrapper;
wrapper = g_object_new (DIA_TYPE_PY_RENDERER, NULL);
wrapper->self = self;
return DIA_RENDERER (wrapper);
}
/*
* GObject boiler plate
*/
static void dia_py_renderer_class_init (DiaPyRendererClass *klass);
GType
dia_py_renderer_get_type (void)
{
static GType object_type = 0;
if (!object_type)
{
static const GTypeInfo object_info =
{
sizeof (DiaPyRendererClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) dia_py_renderer_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (DiaPyRenderer),
0, /* n_preallocs */
NULL /* init */
};
object_type = g_type_register_static (DIA_TYPE_RENDERER,
"DiaPyRenderer",
&object_info, 0);
}
return object_type;
}
static void
dia_py_renderer_finalize (GObject *object)
{
DiaPyRenderer *renderer = DIA_PY_RENDERER (object);
if (renderer->filename)
g_free (renderer->filename);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
dia_py_renderer_class_init (DiaPyRendererClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->finalize = dia_py_renderer_finalize;
/* all defined members from above */
/* renderer members */
renderer_class->begin_render = begin_render;
renderer_class->end_render = end_render;
renderer_class->draw_object = draw_object;
renderer_class->set_linewidth = set_linewidth;
renderer_class->set_linecaps = set_linecaps;
renderer_class->set_linejoin = set_linejoin;
renderer_class->set_linestyle = set_linestyle;
renderer_class->set_dashlength = set_dashlength;
renderer_class->set_fillstyle = set_fillstyle;
renderer_class->set_font = set_font;
renderer_class->draw_line = draw_line;
renderer_class->fill_polygon = fill_polygon;
renderer_class->draw_rect = draw_rect;
renderer_class->fill_rect = fill_rect;
renderer_class->draw_arc = draw_arc;
renderer_class->fill_arc = fill_arc;
renderer_class->draw_ellipse = draw_ellipse;
renderer_class->fill_ellipse = fill_ellipse;
renderer_class->draw_string = draw_string;
renderer_class->draw_image = draw_image;
/* medium level functions */
renderer_class->draw_rect = draw_rect;
renderer_class->draw_polyline = draw_polyline;
renderer_class->draw_polygon = draw_polygon;
renderer_class->draw_bezier = draw_bezier;
renderer_class->fill_bezier = fill_bezier;
/* highest level functions */
renderer_class->draw_rounded_rect = draw_rounded_rect;
renderer_class->fill_rounded_rect = fill_rounded_rect;
}
# PyDia SVG+CSS Renderer
# Copyright (c) 2011 Max Wahler <m...@wwwahler.de>
#
# This SVG(Z) renderer is based on the PyDia SVG renderer by Hans Breuer.
#
# Instead of writing all attributes inline, a separate CSS file is generated
# which contain the color/stroke attributes of objects that have an id meta
# info field.
#
# With this, it's possible to style objects differently after the diagram
# was exported. This is useful for example when the SVG is embedded in a
# HTML page and single elements should be highlighted on actions.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import sys, string, dia
class SvgCssRenderer :
def __init__ (self) :
self.f = None
self.line_width = 0.1
self.line_caps = 0
self.line_join = 0
self.line_style = 0
self.dash_length = 0
self.scale = 20.0
self.css = None
self.current_id = None
self.current_attributes = None
def _open(self, filename) :
self.f = open(filename, "w")
self.css = open(filename + ".css", "w")
def begin_render (self, data, filename) :
self._open (filename)
r = data.extents
xofs = - r[0]
yofs = - r[1]
self.f.write('''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created by diasvgcss.py -->
<svg width="%.3fcm" height="%.3fcm" viewBox="%.3f %.3f %.3f %.3f"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
''' % (r.right - r.left, r.bottom - r.top, r[0] * self.scale, r[1] * self.scale, (r[2] - r[0]) * self.scale, (r[3] - r[1]) * self.scale))
#self.f.write("<!-- %s -->\n" % (str(data.extents)))
#self.f.write("<!-- %s -->\n" % (data.active_layer.name))
def end_render (self) :
self.f.write('</svg>')
self.f.close()
self.css.close()
def draw_object (self, object, matrix) :
self.f.write('<g')
# write id (class) and name attributes
self.current_attributes = {}
meta = object.properties['meta'].value
if meta is not None and meta.has_key('id') :
self.f.write(' class="%s"' % meta['id'])
self.current_id = meta['id']
else :
self.current_id = None
if object.type.name != '' :
self.f.write(' name="%s"' % object.type.name)
self.f.write('>\n')
# don't forget to render the object
object.draw (self)
self.f.write("</g>\n\n");
# write css content
if self.current_id is not None :
self.css.write('.%s {\n' % self.current_id)
for key in self.current_attributes.keys() :
self.css.write('\t%s: %s;\n' % (key, self.current_attributes[key]))
self.css.write('}\n\n')
def set_linewidth (self, width) :
if width < 0.001 : # zero line width is invisble ?
self.line_width = 0.001
else :
self.line_width = width
def set_linecaps (self, mode) :
self.line_caps = mode
def set_linejoin (self, mode) :
self.line_join = mode
def set_linestyle (self, style) :
self.line_style = style
def set_dashlength (self, length) :
self.dash_length = length
def set_fillstyle (self, style) :
# currently only 'solid' so not used anywhere else
self.fill_style = style
def set_font (self, font, size) :
self.font = font
self.font_size = size
def draw_rounded_rect (self, rect, color, rounding) :
self.f.write('<rect x="%.3f" y="%.3f" width="%.3f" height="%.3f" fill="none" %s stroke-width="%.3f" %s rx="%.3f" ry="%.3f" />\n' \
% ( rect.left * self.scale, rect.top * self.scale,
(rect.right - rect.left) * self.scale, (rect.bottom - rect.top) * self.scale,
self._color_attr('stroke', color), self.line_width * self.scale, self._stroke_style(), rounding * self.scale, rounding * self.scale))
def fill_rounded_rect (self, rect, color, rounding) :
self.f.write('<rect x="%.3f" y="%.3f" width="%.3f" height="%.3f" stroke="none" %s rx="%.3f" ry="%.3f" />\n' \
% ( rect.left * self.scale, rect.top * self.scale,
(rect.right - rect.left) * self.scale, (rect.bottom - rect.top) * self.scale,
self._color_attr('fill', color), rounding * self.scale, rounding * self.scale))
def draw_line (self, start, end, color) :
self.f.write('<line x1="%.3f" y1="%.3f" x2="%.3f" y2="%.3f" %s stroke-width="%.3f" %s/>\n' \
% (start.x * self.scale, start.y * self.scale, end.x * self.scale, end.y * self.scale, self._color_attr('stroke', color), self.line_width * self.scale, self._stroke_style()))
def draw_polyline (self, points, color) :
self.f.write('<polyline fill="none" %s stroke-width="%.3f" %s points="' \
% (self._color_attr('stroke', color), self.line_width * self.scale, self._stroke_style()))
for pt in points :
self.f.write ('%.3f,%.3f ' % (pt.x * self.scale, pt.y * self.scale))
self.f.write('"/>\n')
def draw_polygon (self, points, color) :
self.f.write('<polygon fill="none" %s stroke-width="%.3f" %s points="' \
% (self._color_attr('stroke', color), self.line_width * self.scale, self._stroke_style()))
for pt in points :
self.f.write ('%.3f,%.3f ' % (pt.x * self.scale, pt.y * self.scale))
self.f.write('"/>\n')
def fill_polygon (self, points, color) :
self.f.write('<polygon %s stroke="none" stroke-width="%.3f" points="' \
% (self._color_attr('fill', color), self.line_width * self.scale))
for pt in points :
self.f.write ('%.3f,%.3f ' % (pt.x * self.scale, pt.y * self.scale))
self.f.write('"/>\n')
def draw_rect (self, rect, color) :
self.f.write('<rect x="%.3f" y="%.3f" width="%.3f" height="%.3f" fill="none" %s stroke-width="%.3f" %s/>\n' \
% ( rect.left * self.scale, rect.top * self.scale,
(rect.right - rect.left) * self.scale, (rect.bottom - rect.top) * self.scale,
self._color_attr('stroke', color), self.line_width * self.scale, self._stroke_style()))
def fill_rect (self, rect, color) :
self.f.write('<rect x="%.3f" y="%.3f" width="%.3f" height="%.3f" %s stroke="none" stroke-width="0"/>\n' \
% ( rect.left * self.scale, rect.top * self.scale,
(rect.right - rect.left) * self.scale, (rect.bottom - rect.top) * self.scale, self._color_attr('fill', color)))
def _arc (self, center, width, height, angle1, angle2, color, fill=None) :
# not in the renderer interface
import math
mPi180 = math.pi / 180.0
rx = width / 2.0
ry = height / 2.0
sx = center.x + rx * math.cos(mPi180 * angle1)
sy = center.y - ry * math.sin(mPi180 * angle1)
ex = center.x + rx * math.cos(mPi180 * angle2)
ey = center.y - ry * math.sin(mPi180 * angle2)
largearc = (angle2 - angle1 >= 180)
sweep = 0 # always draw in negative direction
if not fill :
self.f.write('<path %s fill="none" stroke-width="%.3f" %s' \
% (self._color_attr('stroke', color), self.line_width * self.scale, self._stroke_style()))
else :
self.f.write('<path stroke="none" %s' % (self._color_attr('fill', color)))
# moveto sx,sy arc rx,ry x-axis-rotation large-arc-flag,sweep-flag ex,ey
self.f.write(' d ="M %.3f,%.3f A %.3f,%.3f 0 %d,%d %.3f,%.3f ' % (sx * self.scale, sy * self.scale, rx * self.scale, ry * self.scale, largearc, sweep, ex * self.scale, ey * self.scale))
self.f.write('"/>\n')
def draw_arc (self, center, width, height, angle1, angle2, color) :
self._arc(center, width, height, angle1, angle2, color)
def fill_arc (self, center, width, height, angle1, angle2, color) :
self._arc(center, width, height, angle1, angle2, color, 1)
def draw_ellipse (self, center, width, height, color) :
self.f.write('<ellipse cx="%.3f" cy="%.3f" rx="%.3f" ry="%.3f" fill="none" %s stroke-width="%.3f" %s/>\n' \
% (center.x * self.scale, center.y * self.scale, (width / 2) * self.scale, (height / 2) * self.scale, self._color_attr('stroke', color), self.line_width * self.scale, self._stroke_style()))
def fill_ellipse (self, center, width, height, color) :
self.f.write('<ellipse cx="%.3f" cy="%.3f" rx="%.3f" ry="%.3f" %s stroke="none" />\n' \
% (center.x * self.scale, center.y * self.scale, (width / 2) * self.scale, (height / 2) * self.scale, self._color_attr('fill', color)))
def draw_bezier (self, bezpoints, color) :
self.f.write('<path %s fill="none" stroke-width="%.3f" %s d="' \
% (self._color_attr('stroke', color), self.line_width * self.scale, self._stroke_style()))
for bp in bezpoints :
if bp.type == 0 : # BEZ_MOVE_TO
self.f.write('M %.3f,%.3f ' % (bp.p1.x * self.scale, bp.p1.y * self.scale))
elif bp.type == 1 : # BEZ_LINE_TO
self.f.write('L %.3f,%.3f ' % (bp.p1.x * self.scale, bp.p1.y * self.scale))
elif bp.type == 2 : # BEZ_CURVE_TO
self.f.write ('C %.3f,%.3f %.3f,%.3f %.3f,%.3f ' \
% (bp.p1.x * self.scale, bp.p1.y * self.scale, bp.p2.x * self.scale, bp.p2.y * self.scale, bp.p3.x * self.scale, bp.p3.y * self.scale))
else :
dia.message(2, "Invalid BezPoint type (%d)" * bp.type)
self.f.write('"/>\n')
def fill_bezier (self, bezpoints, color) :
self.f.write('<path stroke="none" %s stroke-width="%.3f" d="' \
% (self._color_attr('fill', color), self.line_width * self.scale))
for bp in bezpoints :
if bp.type == 0 : # BEZ_MOVE_TO
self.f.write('M %.3f,%.3f ' % (bp.p1.x * self.scale, bp.p1.y * self.scale))
elif bp.type == 1 : # BEZ_LINE_TO
self.f.write('L %.3f,%.3f ' % (bp.p1.x * self.scale, bp.p1.y * self.scale))
elif bp.type == 2 : # BEZ_CURVE_TO
self.f.write ('C %.3f,%.3f %.3f,%.3f %.3f,%.3f ' \
% (bp.p1.x * self.scale, bp.p1.y * self.scale, bp.p2.x * self.scale, bp.p2.y * self.scale, bp.p3.x * self.scale, bp.p3.y * self.scale))
else :
dia.message(2, "Invalid BezPoint type (%d)" * bp.type)
self.f.write('"/>\n')
def draw_string (self, text, pos, alignment, color) :
if len(text) < 1 :
return # shouldn'this be done at the higher level
talign = ('start', 'middle', 'end') [alignment]
fstyle = ('normal', 'italic', 'oblique') [self.font.style & 0x03]
fweight = (400, 200, 300, 500, 600, 700, 800, 900) [(self.font.style >> 4) & 0x7]
self.f.write('<text x="%.3f" y="%.3f" stroke="none" %s text-anchor="%s" font-size="%.2f" font-family="%s" font-style="%s" font-weight="%d">\n' \
% (pos.x * self.scale, pos.y * self.scale, self._color_attr('fill', color), talign, self.font_size * self.scale, self.font.family, fstyle, fweight))
# avoid writing XML special characters (ampersand must be first to not break the rest)
for rep in [('&', '&'), ('<', '<'), ('>', '>'), ('"', '"'), ("'", ''')] :
text = string.replace (text, rep[0], rep[1])
self.f.write(text)
self.f.write('</text>\n')
def draw_image (self, point, width, height, image) :
#FIXME : do something better than absolute pathes ?
self.f.write('<image x="%.3f" y="%.3f" width="%.3f" height="%.3f" xlink:href="%s"/>\n' \
% (point.x * self.scale, point.y * self.scale, width * self.scale, height * self.scale, image.uri))
# Helpers, not in the DiaRenderer interface
def _color_attr(self, attr_name, color) :
if self.current_id is not None :
insertion = ''
self.current_attributes[attr_name] = self._rgb(color)
else :
insertion = '%s="%s"' % (attr_name, self._rgb(color))
return insertion
def _rgb(self, color) :
# given a dia color convert to svg color string
rgb = "#%02X%02X%02X" % (int(255 * color.red), int(color.green * 255), int(color.blue * 255))
return rgb
def _stroke_style(self) :
# return the current line style as svg string
dashlen =self.dash_length
# dashlen/style interpretation like the DiaGdkRenderer
dotlen = dashlen * 0.1
caps = self.line_caps
join = self.line_join
style = self.line_style
st = ""
if style == 0 : # LINESTYLE_SOLID
pass
elif style == 1 : # DASHED
st = 'stroke-dasharray="%.2f,%.2f"' % (dashlen * self.scale, dashlen * self.scale)
elif style == 2 : # DASH_DOT,
gaplen = (dashlen - dotlen) / 2.0
st = 'stroke-dasharray="%.2f,%.2f,%.2f,%.2f"' % (dashlen * self.scale, gaplen * self.scale, dotlen * self.scale, gaplen * self.scale)
elif style == 3 : # DASH_DOT_DOT,
gaplen = (dashlen - dotlen) / 3.0
st = 'stroke-dasharray="%.2f,%.2f,%.2f,%.2f,%.2f,%.2f"' % (dashlen * self.scale, gaplen * self.scale, dotlen * self.scale, gaplen * self.scale, dotlen * self.scale, gaplen * self.scale)
elif style == 4 : # DOTTED
st = 'stroke-dasharray="%.2f,%.2f"' % (dotlen * self.scale, dotlen * self.scale)
if join == 0 : # MITER
pass # st = st + ' stroke-linejoin="bevel"'
elif join == 1 : # ROUND
st = st + ' stroke-linejoin="round"'
elif join == 2 : # BEVEL
st = st + ' stroke-linejoin="bevel"'
if caps == 0 : # BUTT
pass # default stroke-linecap="butt"
elif caps == 1 : # ROUND
st = st + ' stroke-linecap="round"'
elif caps == 2 : # PROJECTING
st = st + ' stroke-linecap="square"' # is this the same ?
return st
class SvgzCssRenderer(SvgCssRenderer) :
def _open(self, filename) :
# There is some (here) not wanted behaviour in gzip.open/GzipFile :
# the filename with path is not only used to adress the file but also
# completely stored in the file itself. Correct it here.
import os, os.path, gzip
path, name = os.path.split(filename)
os.chdir(path)
self.f = gzip.open (name, "wb")
# dia-python keeps a reference to the renderer class and uses it on demand
dia.register_export ("SVG+CSS plain", "svg", SvgCssRenderer())
dia.register_export ("SVG+CSS compressed", "svgz", SvgzCssRenderer())
_______________________________________________
dia-list mailing list
dia-list@gnome.org
http://mail.gnome.org/mailman/listinfo/dia-list
FAQ at http://live.gnome.org/Dia/Faq
Main page at http://live.gnome.org/Dia