mirror of https://github.com/Gnucash/gnucash
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
684 lines
21 KiB
684 lines
21 KiB
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
/*
|
|
* gog-renderer-svg.c :
|
|
*
|
|
* Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
* License as published by the Free Software Foundation.
|
|
*
|
|
* 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 <goffice/goffice-config.h>
|
|
#include <goffice/graph/gog-renderer-svg.h>
|
|
#include <goffice/graph/gog-renderer-impl.h>
|
|
#include <goffice/graph/gog-style.h>
|
|
#include <goffice/graph/gog-view.h>
|
|
#include <goffice/utils/go-color.h>
|
|
#include <goffice/utils/go-font.h>
|
|
#include <goffice/utils/go-marker.h>
|
|
#include <goffice/utils/go-units.h>
|
|
|
|
#include <gsf/gsf-libxml.h>
|
|
#include <gsf/gsf-impl-utils.h>
|
|
#include <pango/pangoft2.h>
|
|
|
|
#include <libxml/tree.h>
|
|
|
|
#include <locale.h>
|
|
#include <math.h>
|
|
|
|
#define CC2XML(s) ((const xmlChar *)(s))
|
|
|
|
#define GOG_RENDERER_SVG_TYPE (gog_renderer_svg_get_type ())
|
|
#define GOG_RENDERER_SVG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RENDERER_SVG_TYPE, GogRendererSvg))
|
|
#define IS_GOG_RENDERER_SVG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RENDERER_SVG_TYPE))
|
|
|
|
typedef struct _GogRendererSvg GogRendererSvg;
|
|
|
|
struct _GogRendererSvg {
|
|
GogRenderer base;
|
|
|
|
xmlDocPtr doc;
|
|
xmlNodePtr defs;
|
|
xmlNodePtr current_node;
|
|
GHashTable *table;
|
|
gint grad, pat, img;
|
|
unsigned clip_counter;
|
|
|
|
PangoContext *pango_context;
|
|
};
|
|
|
|
typedef GogRendererClass GogRendererSvgClass;
|
|
|
|
static GObjectClass *parent_klass;
|
|
|
|
static GType gog_renderer_svg_get_type (void);
|
|
|
|
static void
|
|
gog_renderer_svg_finalize (GObject *obj)
|
|
{
|
|
GogRendererSvg *prend = GOG_RENDERER_SVG (obj);
|
|
|
|
if (prend->pango_context != NULL) {
|
|
g_object_unref (prend->pango_context);
|
|
prend->pango_context = NULL;
|
|
}
|
|
|
|
(*parent_klass->finalize) (obj);
|
|
}
|
|
|
|
static void
|
|
gog_renderer_svg_clip_push (GogRenderer *rend, GogRendererClip *clip)
|
|
{
|
|
GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
|
|
char *buf;
|
|
xmlNodePtr child;
|
|
xmlNodePtr node;
|
|
char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
|
|
|
|
prend->clip_counter++;
|
|
|
|
setlocale (LC_NUMERIC, "C");
|
|
node = xmlNewDocNode (prend->doc, NULL, CC2XML("clipPath"), NULL);
|
|
xmlAddChild (prend->defs, node);
|
|
buf = g_strdup_printf ("clip%i", prend->clip_counter);
|
|
xmlNewProp (node, CC2XML("id"), CC2XML(buf));
|
|
g_free (buf);
|
|
child = xmlNewDocNode (prend->doc, NULL, CC2XML("rect"), NULL);
|
|
xmlAddChild (node, child);
|
|
buf = g_strdup_printf ("%g", clip->area.x);
|
|
xmlNewProp (child, CC2XML("x"), CC2XML(buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("%g", clip->area.y);
|
|
xmlNewProp (child, CC2XML("y"), CC2XML(buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("%g", clip->area.w);
|
|
xmlNewProp (child, CC2XML("width"), CC2XML(buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("%g", clip->area.h);
|
|
xmlNewProp (child, CC2XML("height"), CC2XML(buf));
|
|
g_free (buf);
|
|
|
|
node = xmlNewDocNode (prend->doc, NULL, CC2XML("g"), NULL);
|
|
xmlAddChild (prend->current_node, node);
|
|
buf = g_strdup_printf ("url(#clip%i)", prend->clip_counter);
|
|
xmlNewProp (node, CC2XML ("clip-path"), CC2XML (buf));
|
|
g_free (buf);
|
|
setlocale (LC_NUMERIC, old_num_locale);
|
|
g_free (old_num_locale);
|
|
|
|
prend->current_node = node;
|
|
}
|
|
|
|
static void
|
|
gog_renderer_svg_clip_pop (GogRenderer *rend, GogRendererClip *clip)
|
|
{
|
|
GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
|
|
|
|
prend->current_node = prend->current_node->parent;
|
|
}
|
|
|
|
static void
|
|
draw_path (GogRendererSvg *prend, ArtVpath const *path, GString *string)
|
|
{
|
|
for ( ; path->code != ART_END ; path++)
|
|
switch (path->code) {
|
|
case ART_MOVETO_OPEN :
|
|
case ART_MOVETO :
|
|
g_string_append_printf (string, "M%g %g", path->x, path->y);
|
|
break;
|
|
case ART_LINETO :
|
|
g_string_append_printf (string, "L%g %g", path->x, path->y);
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
stroke_dasharray (xmlNodePtr node, ArtVpathDash *dash)
|
|
{
|
|
GString *string;
|
|
int i;
|
|
|
|
if (dash == NULL || dash->n_dash < 1)
|
|
return;
|
|
|
|
string = g_string_new ("");
|
|
for (i = 0; i < dash->n_dash; i++)
|
|
g_string_append_printf (string, i == 0 ? "%g" : " %g", dash->dash[i]);
|
|
xmlNewProp (node, CC2XML ("stroke-dasharray"), CC2XML (string->str));
|
|
g_string_free (string, TRUE);
|
|
}
|
|
|
|
static void
|
|
gog_renderer_svg_draw_path (GogRenderer *renderer, ArtVpath const *path,
|
|
GogViewAllocation const *bound)
|
|
{
|
|
GogRendererSvg *prend = GOG_RENDERER_SVG (renderer);
|
|
GogStyle const *style = renderer->cur_style;
|
|
xmlNodePtr node;
|
|
GString *string;
|
|
char *buf;
|
|
int opacity;
|
|
char *old_num_locale;
|
|
|
|
if (style->line.dash_type == GO_LINE_NONE)
|
|
return;
|
|
|
|
node = xmlNewDocNode (prend->doc, NULL, "path", NULL);
|
|
old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
|
|
|
|
setlocale (LC_NUMERIC, "C");
|
|
xmlAddChild (prend->current_node, node);
|
|
string = g_string_new ("");
|
|
draw_path (prend, path, string);
|
|
xmlNewProp (node, CC2XML ("d"), CC2XML (string->str));
|
|
g_string_free (string, TRUE);
|
|
xmlNewProp (node, CC2XML ("fill"), CC2XML ("none"));
|
|
buf = g_strdup_printf ("%g", gog_renderer_line_size (renderer, style->line.width));
|
|
xmlNewProp (node, CC2XML ("stroke-width"), CC2XML (buf));
|
|
g_free (buf);
|
|
/* TODO: clip dashed lines to prevent possible rsvg crash */
|
|
stroke_dasharray (node, renderer->line_dash);
|
|
buf = g_strdup_printf ("#%06x", style->line.color >> 8);
|
|
xmlNewProp (node, CC2XML ("stroke"), CC2XML (buf));
|
|
g_free (buf);
|
|
opacity = style->line.color & 0xff;
|
|
if (opacity != 255) {
|
|
buf = g_strdup_printf ("%g", (double) opacity / 255.);
|
|
xmlNewProp (node, CC2XML ("stroke-opacity"), CC2XML (buf));
|
|
g_free (buf);
|
|
}
|
|
setlocale (LC_NUMERIC, old_num_locale);
|
|
g_free (old_num_locale);
|
|
}
|
|
|
|
static void
|
|
gog_renderer_svg_draw_polygon (GogRenderer *renderer, ArtVpath const *path,
|
|
gboolean narrow, GogViewAllocation const *bound)
|
|
{
|
|
GogRendererSvg *prend = GOG_RENDERER_SVG (renderer);
|
|
GogStyle const *style = renderer->cur_style;
|
|
gboolean with_outline = (!narrow && style->outline.dash_type != GO_LINE_NONE);
|
|
xmlNodePtr node;
|
|
char *buf, *name, *id;
|
|
int opacity;
|
|
char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
|
|
|
|
setlocale (LC_NUMERIC, "C");
|
|
if (style->fill.type != GOG_FILL_STYLE_NONE || with_outline) {
|
|
GString *string = g_string_new ("");
|
|
node = xmlNewDocNode (prend->doc, NULL, "path", NULL);
|
|
xmlAddChild (prend->current_node, node);
|
|
draw_path (prend, path, string);
|
|
g_string_append (string, "z");
|
|
xmlNewProp (node, CC2XML ("d"), CC2XML (string->str));
|
|
g_string_free (string, TRUE);
|
|
} else
|
|
return;
|
|
|
|
if (style->fill.type != GOG_FILL_STYLE_NONE) {
|
|
|
|
switch (style->fill.type) {
|
|
case GOG_FILL_STYLE_PATTERN: {
|
|
GOColor color;
|
|
if (go_pattern_is_solid (&style->fill.pattern, &color)) {
|
|
buf = g_strdup_printf ("#%06x", color >> 8);
|
|
xmlNewProp (node, CC2XML ("fill"), CC2XML (buf));
|
|
g_free (buf);
|
|
opacity = color & 0xff;
|
|
if (opacity != 255) {
|
|
buf = g_strdup_printf ("%g", (double) opacity / 255.);
|
|
xmlNewProp (node, CC2XML ("fill-opacity"), CC2XML (buf));
|
|
g_free (buf);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GOG_FILL_STYLE_GRADIENT:
|
|
id = g_strdup_printf ("g_%x_%x_%x", style->fill.gradient.dir,
|
|
style->fill.pattern.back, style->fill.pattern.fore);
|
|
name = (char*) g_hash_table_lookup (prend->table, id);
|
|
if (!name) {
|
|
double x1, y1, x2, y2;
|
|
GOColor start, end;
|
|
xmlNodePtr child, stop;
|
|
name = g_strdup_printf ("grad%d", prend->grad++);
|
|
g_hash_table_insert (prend->table, id, name);
|
|
if (style->fill.gradient.dir < 4) {
|
|
x1 = y1 = x2 = 0;
|
|
y2 = 1;
|
|
} else if (style->fill.gradient.dir < 8) {
|
|
x1 = y1 = y2 = 0;
|
|
x2 = 1;
|
|
} else if (style->fill.gradient.dir < 12) {
|
|
x1 = y1 = 0;
|
|
x2 = y2 = 1;
|
|
} else {
|
|
x1 = y2 = 1;
|
|
x2 = y1 = 0;
|
|
}
|
|
child = xmlNewDocNode (prend->doc, NULL, CC2XML ("linearGradient"), NULL);
|
|
xmlAddChild (prend->defs, child);
|
|
xmlNewProp (child, CC2XML ("id"), CC2XML (name));
|
|
xmlNewProp (child, CC2XML ("gradientUnits"), CC2XML ("objectBoundingBox"));
|
|
switch (style->fill.gradient.dir % 4) {
|
|
case 0:
|
|
buf = (char*) "pad";
|
|
start = style->fill.pattern.fore;
|
|
end = style->fill.pattern.back;
|
|
break;
|
|
case 1:
|
|
buf = (char*) "pad";
|
|
start = style->fill.pattern.back;
|
|
end = style->fill.pattern.fore;
|
|
break;
|
|
case 2:
|
|
buf = (char*) "reflect";
|
|
start = style->fill.pattern.fore;
|
|
end = style->fill.pattern.back;
|
|
x2 = x1 + (x2 - x1) / 2;
|
|
y2 = y1 + (y2 - y1) / 2;
|
|
break;
|
|
default:
|
|
buf = (char*) "reflect";
|
|
start = style->fill.pattern.back;
|
|
end = style->fill.pattern.fore;
|
|
x2 = x1 + (x2 - x1) / 2;
|
|
y2 = y1 + (y2 - y1) / 2;
|
|
break;
|
|
}
|
|
xmlNewProp (child, CC2XML ("spreadMethod"), CC2XML (buf));
|
|
buf = g_strdup_printf ("%g", x1);
|
|
xmlNewProp (child, CC2XML ("x1"), CC2XML (buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("%g", y1);
|
|
xmlNewProp (child, CC2XML ("y1"), CC2XML (buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("%g", x2);
|
|
xmlNewProp (child, CC2XML ("x2"), CC2XML (buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("%g", y2);
|
|
xmlNewProp (child, CC2XML ("y2"), CC2XML (buf));
|
|
g_free (buf);
|
|
stop = xmlNewDocNode (prend->doc, NULL, CC2XML ("stop"), NULL);
|
|
xmlAddChild (child, stop);
|
|
xmlNewProp (stop, CC2XML ("offset"), CC2XML ("0"));
|
|
buf = g_strdup_printf ("#%06x", start >> 8);
|
|
xmlNewProp (stop, CC2XML ("stop-color"), CC2XML (buf));
|
|
g_free (buf);
|
|
opacity = start & 0xff;
|
|
if (opacity != 255) {
|
|
buf = g_strdup_printf ("%g", (double) opacity / 255.);
|
|
xmlNewProp (stop, CC2XML ("stop-opacity"), CC2XML (buf));
|
|
g_free (buf);
|
|
}
|
|
stop = xmlNewDocNode (prend->doc, NULL, CC2XML ("stop"), NULL);
|
|
xmlAddChild (child, stop);
|
|
xmlNewProp (stop, CC2XML ("offset"), CC2XML ("1"));
|
|
buf = g_strdup_printf ("#%06x", end >> 8);
|
|
xmlNewProp (stop, CC2XML ("stop-color"), CC2XML (buf));
|
|
g_free (buf);
|
|
opacity = end & 0xff;
|
|
if (opacity != 255) {
|
|
buf = g_strdup_printf ("%g", (double) opacity / 255.);
|
|
xmlNewProp (stop, CC2XML ("stop-opacity"), CC2XML (buf));
|
|
g_free (buf);
|
|
}
|
|
buf = g_strdup_printf ("url(#%s)", name);
|
|
} else {
|
|
buf = g_strdup_printf ("url(#%s)", name);
|
|
g_free (id);
|
|
}
|
|
xmlNewProp (node, CC2XML ("fill"), CC2XML (buf));
|
|
g_free (buf);
|
|
break;
|
|
|
|
case GOG_FILL_STYLE_IMAGE:
|
|
break;
|
|
|
|
case GOG_FILL_STYLE_NONE:
|
|
break; /* impossible */
|
|
}
|
|
}
|
|
else
|
|
xmlNewProp (node, CC2XML ("fill"), CC2XML ("none"));
|
|
|
|
if (with_outline) {
|
|
/* TODO: clip dashed lines to prevent possible rsvg crash */
|
|
stroke_dasharray (node, renderer->outline_dash);
|
|
buf = g_strdup_printf ("%g", gog_renderer_line_size (renderer, style->outline.width));
|
|
xmlNewProp (node, CC2XML ("stroke-width"), CC2XML (buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("#%06x", style->outline.color >> 8);
|
|
xmlNewProp (node, CC2XML ("stroke"), CC2XML (buf));
|
|
g_free (buf);
|
|
opacity = style->outline.color & 0xff;
|
|
if (opacity != 255) {
|
|
buf = g_strdup_printf ("%g", (double) opacity / 255.);
|
|
xmlNewProp (node, CC2XML ("stroke-opacity"), CC2XML (buf));
|
|
g_free (buf);
|
|
}
|
|
} else
|
|
xmlNewProp (node, CC2XML ("stroke"), CC2XML ("none"));
|
|
setlocale (LC_NUMERIC, old_num_locale);
|
|
g_free (old_num_locale);
|
|
}
|
|
|
|
static void
|
|
gog_renderer_svg_draw_marker (GogRenderer *rend, double x, double y)
|
|
{
|
|
GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
|
|
GOMarker *marker = rend->cur_style->marker.mark;
|
|
ArtVpath const *outline_path_raw, *fill_path_raw;
|
|
ArtVpath *outline_path, *fill_path;
|
|
double scaling[6], translation[6], affine[6];
|
|
double half_size;
|
|
xmlNodePtr node;
|
|
GString *string;
|
|
char *buf;
|
|
int opacity;
|
|
char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
|
|
|
|
setlocale (LC_NUMERIC, "C");
|
|
g_return_if_fail (marker != NULL);
|
|
|
|
go_marker_get_paths (marker, &outline_path_raw, &fill_path_raw);
|
|
|
|
if ((outline_path_raw == NULL) ||
|
|
(fill_path_raw == NULL))
|
|
return;
|
|
|
|
half_size = gog_renderer_line_size (rend, marker->size) / 2.0;
|
|
art_affine_scale (scaling, half_size, half_size);
|
|
art_affine_translate (translation, x, y);
|
|
art_affine_multiply (affine, scaling, translation);
|
|
|
|
outline_path = art_vpath_affine_transform (outline_path_raw, affine);
|
|
fill_path = art_vpath_affine_transform (fill_path_raw, affine);
|
|
|
|
node = xmlNewDocNode (prend->doc, NULL, "path", NULL);
|
|
xmlAddChild (prend->current_node, node);
|
|
string = g_string_new ("");
|
|
draw_path (prend, fill_path, string);
|
|
g_string_append (string, "z");
|
|
xmlNewProp (node, CC2XML ("d"), CC2XML (string->str));
|
|
g_string_free (string, TRUE);
|
|
buf = g_strdup_printf ("#%06x", marker->fill_color >> 8);
|
|
xmlNewProp (node, CC2XML ("fill"), CC2XML (buf));
|
|
g_free (buf);
|
|
xmlNewProp (node, CC2XML ("stroke"), CC2XML ("none"));
|
|
opacity = marker->fill_color & 0xff;
|
|
if (opacity != 255) {
|
|
buf = g_strdup_printf ("%g", (double) opacity / 255.);
|
|
xmlNewProp (node, CC2XML ("fill-opacity"), CC2XML (buf));
|
|
g_free (buf);
|
|
}
|
|
|
|
node = xmlNewDocNode (prend->doc, NULL, "path", NULL);
|
|
xmlAddChild (prend->current_node, node);
|
|
string = g_string_new ("");
|
|
draw_path (prend, outline_path, string);
|
|
g_string_append (string, "z");
|
|
xmlNewProp (node, CC2XML ("d"), CC2XML (string->str));
|
|
g_string_free (string, TRUE);
|
|
xmlNewProp (node, CC2XML ("fill"), CC2XML ("none"));
|
|
xmlNewProp (node, CC2XML ("stroke-linecap"), CC2XML ("round"));
|
|
buf = g_strdup_printf ("%g", gog_renderer_line_size (rend, go_marker_get_outline_width (marker)));
|
|
xmlNewProp (node, CC2XML ("stroke-width"), CC2XML (buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("#%06x", marker->outline_color >> 8);
|
|
xmlNewProp (node, CC2XML ("stroke"), CC2XML (buf));
|
|
g_free (buf);
|
|
opacity = marker->outline_color & 0xff;
|
|
if (opacity != 255) {
|
|
buf = g_strdup_printf ("%g", (double) opacity / 255.);
|
|
xmlNewProp (node, CC2XML ("stroke-opacity"), CC2XML (buf));
|
|
g_free (buf);
|
|
}
|
|
|
|
g_free (outline_path);
|
|
g_free (fill_path);
|
|
setlocale (LC_NUMERIC, old_num_locale);
|
|
g_free (old_num_locale);
|
|
}
|
|
|
|
static PangoLayout *
|
|
make_layout (GogRenderer *rend, char const *text)
|
|
{
|
|
GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
|
|
PangoLayout *layout;
|
|
PangoFontDescription const *fd = rend->cur_style->font.font->desc;
|
|
|
|
if (prend->pango_context == NULL) {
|
|
PangoFT2FontMap *font_map = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ());
|
|
/*assume horizontal and vertical resolutions are the same
|
|
* Why ? */
|
|
pango_ft2_font_map_set_resolution (font_map,
|
|
GO_IN_TO_PT((double)1. / gog_renderer_pt2r (rend, 1.0)),
|
|
GO_IN_TO_PT((double)1. / gog_renderer_pt2r (rend, 1.0)));
|
|
prend->pango_context = pango_ft2_font_map_create_context (font_map);
|
|
g_object_unref (font_map);
|
|
}
|
|
|
|
gog_debug (0, {
|
|
char *msg = pango_font_description_to_string (fd);
|
|
g_warning (msg);
|
|
g_free (msg);
|
|
});
|
|
|
|
layout = pango_layout_new (prend->pango_context);
|
|
pango_layout_set_font_description (layout, fd);
|
|
|
|
pango_layout_set_text (layout, text, -1);
|
|
|
|
return layout;
|
|
}
|
|
|
|
static void
|
|
gog_renderer_svg_measure_text (GogRenderer *rend,
|
|
char const *text, GogViewRequisition *size)
|
|
{
|
|
PangoRectangle rect;
|
|
PangoLayout *layout = make_layout (rend, text);
|
|
pango_layout_get_pixel_extents (layout, NULL, &rect);
|
|
g_object_unref (layout);
|
|
size->w = gog_renderer_pt2r (rend, rect.width);
|
|
size->h = gog_renderer_pt2r (rend, rect.height);
|
|
}
|
|
|
|
static void
|
|
gog_renderer_svg_draw_text (GogRenderer *rend, char const *text,
|
|
GogViewAllocation const *pos, GtkAnchorType anchor,
|
|
GogViewAllocation *result)
|
|
{
|
|
GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
|
|
xmlNodePtr node;
|
|
char *buf;
|
|
double x, y;
|
|
int baseline;
|
|
char *old_num_locale;
|
|
PangoRectangle rect;
|
|
PangoLayout* layout = make_layout (rend, "lp");
|
|
PangoFontDescription const *fd = rend->cur_style->font.font->desc;
|
|
PangoLayoutIter* iter =pango_layout_get_iter(layout);
|
|
pango_layout_get_pixel_extents (layout, NULL, &rect);
|
|
x = pos->x;
|
|
/* adjust to the base line */
|
|
y = pos->y;
|
|
baseline = pango_layout_iter_get_baseline(iter);
|
|
pango_layout_iter_get_run_extents(iter, NULL, &rect);
|
|
y += gog_renderer_pt2r(rend, (baseline - rect.y) / PANGO_SCALE);
|
|
pango_layout_iter_free(iter);
|
|
g_object_unref (layout);
|
|
|
|
switch (anchor) {
|
|
case GTK_ANCHOR_CENTER : case GTK_ANCHOR_E : case GTK_ANCHOR_W :
|
|
y -= gog_renderer_pt2r(rend, (double) (rect.height / 2) / PANGO_SCALE);
|
|
break;
|
|
case GTK_ANCHOR_SE : case GTK_ANCHOR_S : case GTK_ANCHOR_SW :
|
|
y -= gog_renderer_pt2r(rend, (double)rect.height / PANGO_SCALE);
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
|
|
node = xmlNewDocNode (prend->doc, NULL, "text", NULL);
|
|
xmlNodeSetContent (node, CC2XML (text));
|
|
old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
|
|
setlocale (LC_NUMERIC, "C");
|
|
xmlAddChild (prend->current_node, node);
|
|
buf = g_strdup_printf ("%g", x);
|
|
xmlNewProp (node, CC2XML ("x"), CC2XML (buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("%g", y);
|
|
xmlNewProp (node, CC2XML ("y"), CC2XML (buf));
|
|
g_free (buf);
|
|
switch (anchor) {
|
|
case GTK_ANCHOR_CENTER : case GTK_ANCHOR_N : case GTK_ANCHOR_S :
|
|
xmlNewProp (node, CC2XML ("text-anchor"), CC2XML ("middle"));
|
|
break;
|
|
case GTK_ANCHOR_NE : case GTK_ANCHOR_SE : case GTK_ANCHOR_E :
|
|
xmlNewProp (node, CC2XML ("text-anchor"), CC2XML ("end"));
|
|
break;
|
|
default : break;
|
|
}
|
|
xmlNewProp (node, CC2XML ("font-family"), CC2XML (pango_font_description_get_family (fd)));
|
|
buf = g_strdup_printf ("%d", (int)(rint (gog_renderer_pt2r(rend, pango_font_description_get_size (fd) / PANGO_SCALE))));
|
|
xmlNewProp (node, CC2XML ("font-size"), CC2XML (buf));
|
|
g_free (buf);
|
|
switch (pango_font_description_get_weight (fd)) {
|
|
case PANGO_WEIGHT_BOLD:
|
|
xmlNewProp (node, CC2XML ("font-weight"), CC2XML ("bold"));
|
|
break;
|
|
case PANGO_WEIGHT_NORMAL: break;
|
|
default:
|
|
buf = g_strdup_printf ("%d", pango_font_description_get_weight (fd));
|
|
xmlNewProp (node, CC2XML ("font-weight"), CC2XML (buf));
|
|
g_free (buf);
|
|
break;
|
|
}
|
|
switch (pango_font_description_get_style (fd)) {
|
|
case PANGO_STYLE_ITALIC:
|
|
xmlNewProp (node, CC2XML ("font-syle"), CC2XML ("italic"));
|
|
break;
|
|
case PANGO_STYLE_OBLIQUE:
|
|
xmlNewProp (node, CC2XML ("font-syle"), CC2XML ("oblique"));
|
|
break;
|
|
default: break;
|
|
}
|
|
setlocale (LC_NUMERIC, old_num_locale);
|
|
g_free (old_num_locale);
|
|
}
|
|
|
|
static void
|
|
gog_renderer_svg_class_init (GogRendererClass *rend_klass)
|
|
{
|
|
GObjectClass *gobject_klass = (GObjectClass *) rend_klass;
|
|
|
|
parent_klass = g_type_class_peek_parent (rend_klass);
|
|
gobject_klass->finalize = gog_renderer_svg_finalize;
|
|
rend_klass->clip_push = gog_renderer_svg_clip_push;
|
|
rend_klass->clip_pop = gog_renderer_svg_clip_pop;
|
|
rend_klass->draw_path = gog_renderer_svg_draw_path;
|
|
rend_klass->draw_polygon = gog_renderer_svg_draw_polygon;
|
|
rend_klass->draw_text = gog_renderer_svg_draw_text;
|
|
rend_klass->draw_marker = gog_renderer_svg_draw_marker;
|
|
rend_klass->measure_text = gog_renderer_svg_measure_text;
|
|
}
|
|
|
|
static GSF_CLASS (GogRendererSvg, gog_renderer_svg,
|
|
gog_renderer_svg_class_init, NULL,
|
|
GOG_RENDERER_TYPE)
|
|
|
|
/**
|
|
* gog_graph_export_to_svg :
|
|
* @graph : #GogGraph
|
|
* @output : #GsfOutput
|
|
* @width :
|
|
* @height :
|
|
*
|
|
* Renders @graph as SVG and stores it in @output.
|
|
*
|
|
* Returns TRUE on success.
|
|
**/
|
|
gboolean
|
|
gog_graph_export_to_svg (GogGraph *graph, GsfOutput *output,
|
|
double width, double height, double scale)
|
|
{
|
|
GogViewAllocation allocation;
|
|
GogRendererSvg *prend;
|
|
xmlNsPtr namespace;
|
|
gboolean success = TRUE;
|
|
char *buf;
|
|
char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
|
|
setlocale (LC_NUMERIC, "C");
|
|
|
|
prend = g_object_new (GOG_RENDERER_SVG_TYPE,
|
|
"model", graph,
|
|
NULL);
|
|
prend->base.scale = scale;
|
|
prend->doc = xmlNewDoc (CC2XML ("1.0"));
|
|
|
|
xmlNewDtd (prend->doc,
|
|
CC2XML ("svg"), CC2XML ("-//W3C//DTD SVG 1.1//EN"),
|
|
CC2XML ("http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"));
|
|
prend->doc->children = xmlNewDocNode (prend->doc, NULL, CC2XML ("svg"), NULL);
|
|
prend->current_node = prend->doc->children;
|
|
prend->defs = xmlNewDocNode (prend->doc, NULL, CC2XML ("defs"), NULL);
|
|
xmlAddChild (prend->doc->children, prend->defs);
|
|
prend->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
prend->grad = prend->pat = prend->img = 0;
|
|
|
|
namespace = xmlNewNs (prend->doc->children, CC2XML ("http://www.w3.org/2000/svg"), NULL);
|
|
xmlSetNs (prend->doc->children, namespace);
|
|
xmlNewProp (prend->doc->children, CC2XML ("version"), CC2XML ("1.1"));
|
|
|
|
namespace = xmlNewNs (prend->doc->children, CC2XML ("http://www.w3.org/1999/xlink"), CC2XML ("xlink"));
|
|
|
|
buf = g_strdup_printf ("%g", width);
|
|
xmlNewProp (prend->doc->children, CC2XML ("width"), CC2XML (buf));
|
|
g_free (buf);
|
|
buf = g_strdup_printf ("%g", height);
|
|
xmlNewProp (prend->doc->children, CC2XML ("height"), CC2XML (buf));
|
|
g_free (buf);
|
|
setlocale (LC_NUMERIC, old_num_locale);
|
|
g_free (old_num_locale);
|
|
|
|
prend->clip_counter = 0;
|
|
allocation.x = 0.;
|
|
allocation.y = 0.;
|
|
allocation.w = width;
|
|
allocation.h = height;
|
|
gog_view_size_allocate (prend->base.view, &allocation);
|
|
gog_view_render (prend->base.view, NULL);
|
|
|
|
if ((!g_hash_table_size (prend->table)) &&
|
|
(prend->clip_counter == 0)) {
|
|
xmlUnlinkNode (prend->defs);
|
|
xmlFreeNode (prend->defs);
|
|
}
|
|
xmlIndentTreeOutput = TRUE;
|
|
if (gsf_xmlDocFormatDump (output, prend->doc, "UTF-8", TRUE) < 0)
|
|
success = FALSE;
|
|
|
|
xmlFreeDoc (prend->doc);
|
|
g_hash_table_destroy (prend->table);
|
|
g_object_unref (prend);
|
|
|
|
return success;
|
|
}
|