From 70f73bedd3d11244db99bfb5bba7ac75574dc317 Mon Sep 17 00:00:00 2001 From: ellson Date: Sat, 10 Jun 2006 01:44:28 +0000 Subject: [PATCH] - add a "core" plugin for renderers that are always available - rename *pangocairo.c to *pango.c --- plugin/core/gvplugin_core.c | 28 ++ plugin/core/gvrender_core_ps.c | 670 ++++++++++++++++++++++++++++++ plugin/core/gvrender_core_svg.c | 623 +++++++++++++++++++++++++++ plugin/gd/gvplugin_gd.c | 4 +- plugin/pango/gvplugin_pango.c | 28 ++ plugin/pango/gvrender_pango.c | 589 ++++++++++++++++++++++++++ plugin/pango/gvtextlayout_pango.c | 108 +++++ 7 files changed, 2048 insertions(+), 2 deletions(-) create mode 100644 plugin/core/gvplugin_core.c create mode 100644 plugin/core/gvrender_core_ps.c create mode 100644 plugin/core/gvrender_core_svg.c create mode 100644 plugin/pango/gvplugin_pango.c create mode 100644 plugin/pango/gvrender_pango.c create mode 100644 plugin/pango/gvtextlayout_pango.c diff --git a/plugin/core/gvplugin_core.c b/plugin/core/gvplugin_core.c new file mode 100644 index 000000000..f53e31e3c --- /dev/null +++ b/plugin/core/gvplugin_core.c @@ -0,0 +1,28 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#include "gvplugin.h" + +extern gvplugin_installed_t gvrender_core_ps_types; +extern gvplugin_installed_t gvrender_core_svg_types; + +static gvplugin_api_t apis[] = { + {API_render, &gvrender_core_ps_types}, + {API_render, &gvrender_core_svg_types}, + {(api_t)0, NULL}, +}; + +gvplugin_library_t gvplugin_core_LTX_library = { "core", apis }; diff --git a/plugin/core/gvrender_core_ps.c b/plugin/core/gvrender_core_ps.c new file mode 100644 index 000000000..491cbde51 --- /dev/null +++ b/plugin/core/gvrender_core_ps.c @@ -0,0 +1,670 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBZ +#include +#ifdef MSWIN32 +#include +#endif +#endif + +#undef HAVE_LIBGD + +#include "textpara.h" +#include "gvplugin_render.h" +#include "gv_ps.h" +#include "const.h" +#include "macros.h" +#ifdef HAVE_LIBGD +#include "gd.h" +#endif + +extern void cat_libfile(FILE * ofp, char **arglib, char **stdlib); +extern void epsf_define(FILE * of); +extern char *ps_string(char *ins, int latin); + +typedef enum { FORMAT_PS, FORMAT_PS2, } format_type; + +static box DBB; +static int isLatin1; +static char setupLatin1; + +static char *last_fontname; +static double last_fontsize; +static gvcolor_t last_color; + +static void psgen_begin_job(GVJ_t * job) +{ + last_fontname = NULL; + last_fontsize = 0.0; + last_color.u.HSV[0] = 0; + last_color.u.HSV[1] = 0; + last_color.u.HSV[2] = 0; + + fprintf(job->output_file, "%%!PS-Adobe-2.0\n"); + fprintf(job->output_file, "%%%%Creator: %s version %s (%s)\n", + job->common->info[0], job->common->info[1], job->common->info[2]); + fprintf(job->output_file, "%%%%For: %s\n", job->common->user); +} + +static void psgen_end_job(GVJ_t * job) +{ + fprintf(job->output_file, "%%%%Trailer\n"); + fprintf(job->output_file, "%%%%Pages: %d\n", job->common->viewNum); + if (job->common->show_boxes == NULL) + fprintf(job->output_file, "%%%%BoundingBox: %d %d %d %d\n", + DBB.LL.x, DBB.LL.y, DBB.UR.x, DBB.UR.y); + fprintf(job->output_file, "end\nrestore\n"); + fprintf(job->output_file, "%%%%EOF\n"); +} + +static void psgen_begin_graph(GVJ_t * job, char *graphname) +{ + setupLatin1 = FALSE; + + if (job->common->viewNum == 0) { + fprintf(job->output_file, "%%%%Title: %s\n", graphname); + fprintf(job->output_file, "%%%%Pages: (atend)\n"); + if (job->common->show_boxes == NULL) + fprintf(job->output_file, "%%%%BoundingBox: (atend)\n"); + fprintf(job->output_file, "%%%%EndComments\nsave\n"); + cat_libfile(job->output_file, job->common->lib, gv_ps_txt); + epsf_define(job->output_file); + } +#ifdef FIXME + isLatin1 = (GD_charset(g) == CHAR_LATIN1); + if (isLatin1 && !setupLatin1) { + fprintf(job->output_file, "setupLatin1\n"); /* as defined in ps header */ + setupLatin1 = TRUE; + } +#endif +#ifdef FIXME + /* Set base URL for relative links (for Distiller >= 3.0) */ + if (((s = agget(g, "href")) && s[0]) + || ((s = agget(g, "URL")) && s[0])) { + fprintf(job->output_file, "[ {Catalog} << /URI << /Base (%s) >> >>\n" + "/PUT pdfmark\n", s); + } +#endif +} + +static void psgen_end_graph(GVJ_t * job) +{ +#if 0 + if (EPSF_contents) { + dtclose(EPSF_contents); + EPSF_contents = 0; + N_EPSF_files = 0; + } + onetime = FALSE; +#endif +} + +static void psgen_begin_layer(GVJ_t * job, char *layername, int layerNum, int numLayers) +{ + fprintf(job->output_file, "%d %d setlayer\n", layerNum, numLayers); +} + +static point sub_points(point p0, point p1) +{ + p0.x -= p1.x; + p0.y -= p1.y; + return p0; +} + +static void psgen_begin_page(GVJ_t * job) +{ + point sz; + box PB, pbr; + + BF2B(job->boundingBox, PB); + + sz = sub_points(PB.UR, PB.LL); + if (job->rotation) { + pbr.LL.x = PB.LL.y; + pbr.LL.y = PB.LL.x; + pbr.UR.x = PB.UR.y; + pbr.UR.y = PB.UR.x; + } + else { + pbr = PB; + } + + if (job->common->viewNum == 0) + DBB = pbr; + else + EXPANDBB(DBB, pbr); + + fprintf(job->output_file, "%%%%Page: %d %d\n", + job->common->viewNum + 1, job->common->viewNum + 1); + if (job->common->show_boxes == NULL) + fprintf(job->output_file, "%%%%PageBoundingBox: %d %d %d %d\n", + pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y); + fprintf(job->output_file, "%%%%PageOrientation: %s\n", + (job->rotation ? "Landscape" : "Portrait")); + if (job->common->show_boxes == NULL) + fprintf(job->output_file, "gsave\n%d %d %d %d boxprim clip newpath\n", + pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y); + fprintf(job->output_file, "gsave %g set_scale\n", job->zoom); + if (job->rotation) { + fprintf(job->output_file, "%d rotate\n", job->rotation); + fprintf(job->output_file, "%g %g translate\n", + -job->pageBox.LL.x + pbr.LL.y / job->zoom, + -job->pageBox.UR.y - pbr.LL.x / job->zoom); + } + else + fprintf(job->output_file, "%g %g translate\n", + -job->pageBox.LL.x + pbr.LL.x / job->zoom, + -job->pageBox.LL.y + pbr.LL.y / job->zoom); + +#if 0 + /* Define the size of the PS canvas */ + if (Output_lang == PDF) { + if (PB.UR.x >= PDFMAX || PB.UR.y >= PDFMAX) + agerr(AGWARN, + "canvas size (%d,%d) exceeds PDF limit (%d)\n" + "\t(suggest setting a bounding box size, see dot(1))\n", + PB.UR.x, PB.UR.y, PDFMAX); + fprintf(job->output_file, "[ /CropBox [%d %d %d %d] /PAGES pdfmark\n", + PB.LL.x, PB.LL.y, PB.UR.x, PB.UR.y); + } +#endif +} + +static void psgen_end_page(GVJ_t * job) +{ + if (job->common->show_boxes) + cat_libfile(job->output_file, NULL, job->common->show_boxes + 1); + /* the showpage is really a no-op, but at least one PS processor + * out there needs to see this literal token. endpage does the real work. + */ + fprintf(job->output_file, "endpage\nshowpage\ngrestore\n"); + fprintf(job->output_file, "%%%%PageTrailer\n"); + fprintf(job->output_file, "%%%%EndPage: %d\n", job->common->viewNum); +} + +static void psgen_begin_cluster(GVJ_t * job, char *clustername, long id) +{ + fprintf(job->output_file, "%% %s\n", clustername); + +#if 0 + /* Embed information for Distiller to generate hyperlinked PDF */ + map_begin_cluster(g); +#endif +} + +static void psgen_begin_node(GVJ_t * job, char *nodename, long id) +{ +#if 0 + /* Embed information for Distiller to generate hyperlinked PDF */ + map_begin_node(n); +#endif +} + +static void +psgen_begin_edge(GVJ_t * job, char *tailname, bool directed, + char *headname, long id) +{ +#if 0 + /* Embed information for Distiller, so it can generate hyperactive PDF */ + map_begin_edge(e); +#endif +} + +static void +psgen_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target) +{ +} + +static void psgen_end_anchor(GVJ_t * job) +{ +} + +static void +ps_set_pen_style(GVJ_t *job) +{ +#if 0 + double penwidth = job->style->penwidth * job->zoom; + char *p, *line, **s = job->rawstyle; + + fprintf(stderr,"%g setlinewidth\n", penwidth); + + while (s && (p = line = *s++)) { + if (streq(line, "setlinewidth")) + continue; + while (*p) + p++; + p++; + while (*p) { + fprintf(stderr,"%s ", p); + while (*p) + p++; + p++; + } +// if (streq(line, "invis")) +// job->style->width = 0; + fprintf(stderr, "%s\n", line); + } +#endif +} + +static void ps_set_color(GVJ_t *job, gvcolor_t *color) +{ + if (color) { + if ( last_color.u.HSV[0] != color->u.HSV[0] + || last_color.u.HSV[1] != color->u.HSV[1] + || last_color.u.HSV[2] != color->u.HSV[2]) { + fprintf(job->output_file, "%.3f %.3f %.3f %scolor\n", + color->u.HSV[0], color->u.HSV[1], color->u.HSV[2], job->objname); + last_color.u.HSV[0] = color->u.HSV[0]; + last_color.u.HSV[1] = color->u.HSV[1]; + last_color.u.HSV[2] = color->u.HSV[2]; + } + } +} + +static void psgen_textpara(GVJ_t * job, pointf p, textpara_t * para) +{ + double adj, sz; + char *str; + + ps_set_color(job, &(job->style->pencolor)); + if (para->fontname) { + sz = para->fontsize; + if (sz != last_fontsize + || last_fontname == NULL + || strcmp(para->fontname, last_fontname) != 0) { + fprintf(job->output_file, "%.2f /%s set_font\n", sz, para->fontname); + last_fontsize = sz; + last_fontname = para->fontname; + } + } + + str = ps_string(para->str,isLatin1); + if (para->xshow) { + switch (para->just) { + case 'l': + break; + case 'r': + p.x -= para->width; + break; + default: + case 'n': + p.x -= para->width / 2; + break; + } + fprintf(job->output_file, "%g %g moveto\n%s\n[%s]\nxshow\n", + p.x, p.y, str, para->xshow); + } else { + switch (para->just) { + case 'l': + adj = 0.0; + break; + case 'r': + adj = -1.0; + break; + default: + case 'n': + adj = -0.5; + break; + } + fprintf(job->output_file, "%g %g moveto %1f %.1f %s alignedtext\n", + p.x, p.y, para->width, adj, str); + } +} + +static void psgen_ellipse(GVJ_t * job, pointf * A, int filled) +{ + /* A[] contains 2 points: the center and corner. */ + + if (filled) { + ps_set_color(job, &(job->style->fillcolor)); + fprintf(job->output_file, "%g %g %g %g ellipse_path fill\n", + A[0].x, A[0].y, fabs(A[1].x - A[0].x), fabs(A[1].y - A[0].y)); + } + ps_set_pen_style(job); + ps_set_color(job, &(job->style->pencolor)); + fprintf(job->output_file, "%g %g %g %g ellipse_path stroke\n", + A[0].x, A[0].y, fabs(A[1].x - A[0].x), fabs(A[1].y - A[0].y)); +} + +static void +psgen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, + int arrow_at_end, int filled) +{ + int j; + + if (filled) { + ps_set_color(job, &(job->style->fillcolor)); + fprintf(job->output_file, "newpath %g %g moveto\n", A[0].x, A[0].y); + for (j = 1; j < n; j += 3) + fprintf(job->output_file, "%g %g %g %g %g %g curveto\n", + A[j].x, A[j].y, A[j + 1].x, A[j + 1].y, A[j + 2].x, + A[j + 2].y); + fprintf(job->output_file, "closepath fill\n"); + } + ps_set_pen_style(job); + ps_set_color(job, &(job->style->pencolor)); + fprintf(job->output_file, "newpath %g %g moveto\n", A[0].x, A[0].y); + for (j = 1; j < n; j += 3) + fprintf(job->output_file, "%g %g %g %g %g %g curveto\n", + A[j].x, A[j].y, A[j + 1].x, A[j + 1].y, A[j + 2].x, + A[j + 2].y); + fprintf(job->output_file, "stroke\n"); +} + +static void psgen_polygon(GVJ_t * job, pointf * A, int n, int filled) +{ + int j; + + if (filled) { + ps_set_color(job, &(job->style->fillcolor)); + fprintf(job->output_file, "newpath %g %g moveto\n", A[0].x, A[0].y); + for (j = 1; j < n; j++) + fprintf(job->output_file, "%g %g lineto\n", A[j].x, A[j].y); + fprintf(job->output_file, "closepath fill\n"); + } + ps_set_pen_style(job); + ps_set_color(job, &(job->style->pencolor)); + fprintf(job->output_file, "newpath %g %g moveto\n", A[0].x, A[0].y); + for (j = 1; j < n; j++) + fprintf(job->output_file, "%g %g lineto\n", A[j].x, A[j].y); + fprintf(job->output_file, "closepath stroke\n"); +} + +static void psgen_polyline(GVJ_t * job, pointf * A, int n) +{ + int j; + + ps_set_pen_style(job); + ps_set_color(job, &(job->style->pencolor)); + fprintf(job->output_file, "newpath %g %g moveto\n", A[0].x, A[0].y); + for (j = 1; j < n; j++) + fprintf(job->output_file, "%g %g lineto\n", A[j].x, A[j].y); + fprintf(job->output_file, "stroke\n"); +} + +static void psgen_comment(GVJ_t * job, char *str) +{ + fprintf(job->output_file, "%% %s\n", str); +} + +static void ps_freeimage_gd (void *data) +{ +#ifdef HAVE_LIBGD + gdImageDestroy((gdImagePtr)data); +#endif +} + +static void ps_freeimage_ps (void *data) +{ +#if 0 + free (data); +#endif +} + +#ifdef HAVE_LIBGD +static void writePSBitmap (GVJ_t *job, gdImagePtr im, boxf b) +{ + int x, y, px; + + fprintf(job->output_file, "gsave\n"); + + /* this sets the position of the image */ + fprintf(job->output_file, "%g %g translate %% lower-left coordinate\n", b.LL.x, b.LL.y); + + /* this sets the rendered size to fit the box */ + fprintf(job->output_file,"%g %g scale\n", b.UR.x - b.LL.x, b.UR.y - b.LL.y); + + /* xsize ysize bits-per-sample [matrix] */ + fprintf(job->output_file, "%d %d 8 [%d 0 0 %d 0 %d]\n", im->sx, im->sy, + im->sx, -(im->sy), im->sy); + + fprintf(job->output_file, "{<\n"); + for (y = 0; y < im->sy; y++) { + for (x = 0; x < im->sx; x++) { + if (im->trueColor) { + px = gdImageTrueColorPixel(im, x, y); + fprintf(job->output_file, "%02x%02x%02x", + gdTrueColorGetRed(px), + gdTrueColorGetGreen(px), + gdTrueColorGetBlue(px)); + } + else { + px = gdImagePalettePixel(im, x, y); + fprintf(job->output_file, "%02x%02x%02x", + im->red[px], + im->green[px], + im->blue[px]); + } + } + fprintf(job->output_file, "\n"); + } + + fprintf(job->output_file, ">}\n"); + fprintf(job->output_file, "false 3 colorimage\n"); + + fprintf(job->output_file, "grestore\n"); + +} +#endif + + +/* ps_usershape: + * Images for postscript are complicated by the old epsf shape, as + * well as user-defined shapes using postscript code. + * If the name is custom, we look for the image stored in the + * current node's shapefile attribute. + * Else we see if name is a user-defined postscript function + * Else we assume name is the name of the image. This occurs when + * the image is part of an html label. + */ +static void +psgen_usershape(GVJ_t * job, usershape_t *us, boxf b, bool filled) +{ + int j; +#ifdef HAVE_LIBGD + gdImagePtr gd_img = NULL; +#endif +#ifdef XXX_PS + ps_image_t *ps_img = NULL; +#endif + point offset; + + if (!us->f) { +#ifdef XXX_PS + if (find_user_shape(us->name)) { + if (filled) { + ps_begin_context(); + ps_set_color(S[SP].fillcolor); + fprintf(job->output_file, "[ "); + for (j = 0; j < n; j++) + fprintf(job->output_file, "%d %d ", A[j].x, A[j].y); + fprintf(job->output_file, "%d %d ", A[0].x, A[0].y); + fprintf(job->output_file, "] %d true %s\n", n, us->name); + ps_end_context(); + } + fprintf(job->output_file, "[ "); + for (j = 0; j < n; j++) + fprintf(job->output_file, "%d %d ", A[j].x, A[j].y); + fprintf(job->output_file, "%d %d ", A[0].x, A[0].y); + fprintf(job->output_file, "] %d false %s\n", n, us->name); + } + else { /* name not find by find_ser_shape */ } +#endif + return; + } + + if (us->data) { + if (us->datafree == ps_freeimage_gd) { +#ifdef HAVE_LIBGD + gd_img = (gdImagePtr)(us->data); /* use cached data */ +#endif + } + else if (us->datafree == ps_freeimage_ps) { +#ifdef XXX_PS + ps_img = (ps_image_t *)(us->data); /* use cached data */ +#endif + } + else { + us->datafree(us->data); /* free incompatible cache data */ + us->data = NULL; + } + } + +#ifdef HAVE_LIBGD +#ifdef XXX_PS + if (!ps_img && !gd_img) { /* read file into cache */ +#else + if (!gd_img) { /* read file into cache */ +#endif +#else +#ifdef XXX_PS + if (!ps_img) { /* read file into cache */ +#else + if (false) { /* nothing to do */ +#endif +#endif + fseek(us->f, 0, SEEK_SET); + switch (us->type) { +#ifdef HAVE_LIBGD +#ifdef HAVE_GD_PNG + case FT_PNG: + gd_img = gdImageCreateFromPng(us->f); + break; +#endif +#ifdef HAVE_GD_GIF + case FT_GIF: + gd_img = gdImageCreateFromGif(us->f); + break; +#endif +#ifdef HAVE_GD_JPEG + case FT_JPEG: + gd_img = gdImageCreateFromJpeg(us->f); + break; +#endif +#endif +#ifdef XXX_PS + case FT_PS: + case FT_EPS: + ps_img = ps_usershape_to_image(us->name); + break; +#endif + default: + break; + } +#ifdef HAVE_LIBGD + if (gd_img) { + us->data = (void*)gd_img; + us->datafree = ps_freeimage_gd; + } +#endif +#ifdef XXX_PS + if (ps_img) { + us->data = (void*)ps_img; + us->datafree = ps_freeimage_ps; + } +#endif + } + +#ifdef XXX_PS + if (ps_img) { + ps_begin_context(); + offset.x = -ps_img->origin.x - (ps_img->size.x) / 2; + offset.y = -ps_img->origin.y - (ps_img->size.y) / 2; + fprintf(job->output_file, "%d %d translate newpath\n", + ND_coord_i(Curnode).x + offset.x, + ND_coord_i(Curnode).y + offset.y); + if (ps_img->must_inline) + epsf_emit_body(ps_img, job->output_file); + else + fprintf(job->output_file, "user_shape_%d\n", ps_img->macro_id); + ps_end_context(); + return; + } +#endif + +#ifdef HAVE_LIBGD + if (gd_img) { + writePSBitmap (job, gd_img, b); + return; + } +#endif + +#if 0 +/* FIXME */ + /* some other type of image */ + agerr(AGERR, "usershape %s is not supported in PostScript output\n", us->name); +#endif +} + +static gvrender_engine_t psgen_engine = { + psgen_begin_job, + psgen_end_job, + psgen_begin_graph, + psgen_end_graph, + psgen_begin_layer, + 0, /* psgen_end_layer */ + psgen_begin_page, + psgen_end_page, + psgen_begin_cluster, + 0, /* psgen_end_cluster */ + 0, /* psgen_begin_nodes */ + 0, /* psgen_end_nodes */ + 0, /* psgen_begin_edges */ + 0, /* psgen_end_edges */ + psgen_begin_node, + 0, /* psgen_end_node */ + psgen_begin_edge, + 0, /* psgen_end_edge */ + psgen_begin_anchor, + psgen_end_anchor, + psgen_textpara, + 0, /* psgen_resolve_color */ + psgen_ellipse, + psgen_polygon, + psgen_bezier, + psgen_polyline, + psgen_comment, + psgen_usershape +}; + +static gvrender_features_t psgen_features = { + GVRENDER_DOES_MULTIGRAPH_OUTPUT_FILES + | GVRENDER_DOES_TRANSFORM, + DEFAULT_PRINT_MARGIN, /* default margin - points */ + {72.,72.}, /* default dpi */ + NULL, /* knowncolors */ + 0, /* sizeof knowncolors */ + HSV_DOUBLE, /* color_type */ +}; + +gvplugin_installed_t gvrender_core_ps_types[] = { + {FORMAT_PS, "ps", -2, &psgen_engine, &psgen_features}, + {FORMAT_PS2, "ps2", -2, &psgen_engine, &psgen_features}, + {0, NULL, 0, NULL, NULL} +}; diff --git a/plugin/core/gvrender_core_svg.c b/plugin/core/gvrender_core_svg.c new file mode 100644 index 000000000..ca2c3c772 --- /dev/null +++ b/plugin/core/gvrender_core_svg.c @@ -0,0 +1,623 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBZ +#include +#endif +#ifdef MSWIN32 +#include +#endif + +#include "macros.h" +#include "const.h" +#include "types.h" + +#include "gvplugin_render.h" + +typedef enum { FORMAT_SVG, FORMAT_SVGZ, } format_type; + +extern char *xml_string(char *str); + +static char *GraphName; + +/* SVG dash array */ +static char *sdarray = "5,2"; +/* SVG dot array */ +static char *sdotarray = "1,5"; + +static void svggen_fputs(GVJ_t * job, char *s) +{ + int len; + + len = strlen(s); + switch (job->render.id) { + case FORMAT_SVGZ: +#ifdef HAVE_LIBZ + gzwrite((gzFile *) (job->output_file), s, (unsigned) len); +#endif + break; + case FORMAT_SVG: + fwrite(s, sizeof(char), (unsigned) len, job->output_file); + break; + } +} + +/* svggen_printf: + * Note that this function is unsafe due to the fixed buffer size. + * It should only be used when the caller is sure the input will not + * overflow the buffer. In particular, it should be avoided for + * input coming from users. Also, if vsnprintf is available, the + * code should check for return values to use it safely. + */ +static void svggen_printf(GVJ_t * job, const char *format, ...) +{ + char buf[BUFSIZ]; + va_list argp; + + va_start(argp, format); +#ifdef HAVE_VSNPRINTF + (void) vsnprintf(buf, sizeof(buf), format, argp); +#else + (void) vsprintf(buf, format, argp); +#endif + va_end(argp); + + svggen_fputs(job, buf); +} + +static void svggen_bzptarray(GVJ_t * job, pointf * A, int n) +{ + int i; + char c; + + c = 'M'; /* first point */ + for (i = 0; i < n; i++) { + svggen_printf(job, "%c%g,%g", c, A[i].x, A[i].y); + if (i == 0) + c = 'C'; /* second point */ + else + c = ' '; /* remaining points */ + } +} + +static void svggen_print_color(GVJ_t * job, gvcolor_t color) +{ + static char buf[SMALLBUF]; + + switch (color.type) { + case COLOR_STRING: + svggen_fputs(job, color.u.string); + break; + case RGBA_BYTE: + sprintf(buf, "#%02x%02x%02x", + color.u.rgba[0], color.u.rgba[1], color.u.rgba[2]); + svggen_fputs(job, buf); + break; + default: + assert(0); /* internal error */ + } +} + +static void svggen_font(GVJ_t * job) +{ + gvstyle_t *style = job->style; + char buf[BUFSIZ]; + int needstyle = 0; + + strcpy(buf, " style=\""); + if (strcasecmp(style->fontfam, DEFAULT_FONTNAME)) { + sprintf(buf + strlen(buf), "font-family:%s;", style->fontfam); + needstyle++; + } + if (style->fontsz != DEFAULT_FONTSIZE) { + sprintf(buf + strlen(buf), "font-size:%.2f;", (style->fontsz)); + needstyle++; + } + switch (style->pencolor.type) { + case COLOR_STRING: + if (strcasecmp(style->pencolor.u.string, "black")) { + sprintf(buf + strlen(buf), "fill:%s;", + style->pencolor.u.string); + needstyle++; + } + break; + case RGBA_BYTE: + sprintf(buf + strlen(buf), "fill:#%02x%02x%02x;", + style->pencolor.u.rgba[0], + style->pencolor.u.rgba[1], style->pencolor.u.rgba[2]); + needstyle++; + break; + default: + assert(0); /* internal error */ + } + if (needstyle) { + strcat(buf, "\""); + svggen_fputs(job, buf); + } +} + + +static void svggen_grstyle(GVJ_t * job, int filled) +{ + gvstyle_t *style = job->style; + + svggen_fputs(job, " style=\"fill:"); + if (filled) + svggen_print_color(job, style->fillcolor); + else + svggen_fputs(job, "none"); + svggen_fputs(job, ";stroke:"); + svggen_print_color(job, style->pencolor); + if (style->penwidth != PENWIDTH_NORMAL) + svggen_printf(job, ";stroke-width:%g", style->penwidth); + if (style->pen == PEN_DASHED) { + svggen_printf(job, ";stroke-dasharray:%s", sdarray); + } else if (style->pen == PEN_DOTTED) { + svggen_printf(job, ";stroke-dasharray:%s", sdotarray); + } + svggen_fputs(job, ";\""); +} + +static void svggen_comment(GVJ_t * job, char *str) +{ + svggen_fputs(job, "\n"); +} + +static void svggen_begin_job(GVJ_t * job) +{ +#if HAVE_LIBZ + int fd; +#endif + + switch (job->render.id) { + case FORMAT_SVGZ: +#if HAVE_LIBZ + /* open dup so can gzclose independent of FILE close */ + fd = dup(fileno(job->output_file)); +#ifdef HAVE_SETMODE +#ifdef O_BINARY + /* + * Windows will do \n -> \r\n translations on + * stdout unless told otherwise. + */ + setmode(fd, O_BINARY); +#endif +#endif + + job->output_file = (FILE *) (gzdopen(fd, "wb")); + if (!job->output_file) { + (job->common->errorfn) ("Error opening compressed output file\n"); + exit(1); + } + break; +#else + (job->gvc->errorfn) ("No libz support.\n"); + exit(1); +#endif + case FORMAT_SVG: + break; + } + + svggen_fputs(job, + "\n"); + svggen_fputs(job, + "\n]"); + + svggen_fputs(job, ">\n\n"); +} + +static void svggen_begin_graph(GVJ_t * job, char *graphname) +{ + GraphName = graphname; + + svggen_fputs(job, "\n", job->pagesArraySize.x * job->pagesArraySize.y); + + if (ROUND(job->dpi.x) == POINTS_PER_INCH && ROUND(job->dpi.y) == POINTS_PER_INCH) { + svggen_printf(job, "width, job->height); + } + else { + svggen_printf(job, "dpi.x * job->width / POINTS_PER_INCH), + ROUND(job->dpi.y * job->height / POINTS_PER_INCH)); + } + /* establish absolute units in points */ + svggen_printf(job, " viewBox = \"%d %d %d %d\"\n", 0, 0, job->width, job->height); + /* namespace of svg */ + svggen_fputs(job, " xmlns=\"http://www.w3.org/2000/svg\""); + /* namespace of xlink */ + svggen_fputs(job, " xmlns:xlink=\"http://www.w3.org/1999/xlink\""); + svggen_fputs(job, ">\n"); +} + +static void svggen_end_graph(GVJ_t * job) +{ + svggen_fputs(job, "\n"); + switch (job->render.id) { + case FORMAT_SVGZ: +#ifdef HAVE_LIBZ + gzclose((gzFile *) (job->output_file)); + break; +#else + (job->gvc->errorfn) ("No libz support\n"); + exit(1); +#endif + case FORMAT_SVG: + break; + } +} + +static void svggen_begin_layer(GVJ_t * job, char *layername, int layerNum, int numLayers) +{ + svggen_fputs(job, "\n"); +} + +static void svggen_end_layer(GVJ_t * job) +{ + svggen_fputs(job, "\n"); +} + +static void svggen_begin_page(GVJ_t * job) +{ + /* its really just a page of the graph, but its still a graph, + * and it is the entire graph if we're not currently paging */ + svggen_printf(job, "common->viewNum + 1); + if (job->zoom != 1.0) + svggen_printf(job, " transform = \"scale(%f)\"\n", job->zoom); + if (job->rotation) { + svggen_printf(job, "transform=\"rotate(-90 %g %g)\" ", 0, 0); + } + /* default style */ + svggen_fputs(job, " style=\"font-family:"); + svggen_fputs(job, job->style->fontfam); + svggen_printf(job, ";font-size:%.2f;\">\n", job->style->fontsz); + if (GraphName[0]) + svggen_fputs(job, xml_string(GraphName)); + svggen_fputs(job, ""); + svggen_fputs(job, xml_string(GraphName)); + svggen_fputs(job, "\n"); +} + +static void svggen_end_page(GVJ_t * job) +{ + svggen_fputs(job, "\n"); +} + +static void svggen_begin_cluster(GVJ_t * job, char *clustername, long id) +{ + svggen_printf(job, "", id); + svggen_fputs(job, ""); + svggen_fputs(job, xml_string(clustername)); + svggen_fputs(job, "\n"); +} + +static void svggen_end_cluster(GVJ_t * job) +{ + svggen_fputs(job, "\n"); +} + +static void svggen_begin_node(GVJ_t * job, char *nodename, long id) +{ + svggen_printf(job, "", id); + svggen_fputs(job, ""); + svggen_fputs(job, xml_string(nodename)); + svggen_fputs(job, "\n"); +} + +static void svggen_end_node(GVJ_t * job) +{ + svggen_fputs(job, "\n"); +} + +static void +svggen_begin_edge(GVJ_t * job, char *tailname, bool directed, + char *headname, long id) +{ + char *edgeop; + + svggen_printf(job, "", id); + if (directed) + edgeop = "->"; + else + edgeop = "--"; + svggen_fputs(job, ""); + svggen_fputs(job, xml_string(tailname)); + svggen_fputs(job, edgeop); + /* can't do this in single svggen_printf because + * xml_string's buffer gets reused. */ + svggen_fputs(job, xml_string(headname)); + svggen_fputs(job, "\n"); +} + +static void svggen_end_edge(GVJ_t * job) +{ + svggen_fputs(job, "\n"); +} + +static void +svggen_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target) +{ + svggen_fputs(job, "\n"); +} + +static void svggen_end_anchor(GVJ_t * job) +{ + svggen_fputs(job, "\n"); +} + +static void svggen_textpara(GVJ_t * job, pointf p, textpara_t * para) +{ + char *anchor; + + switch (para->just) { + case 'l': + anchor = "start"; + break; + case 'r': + anchor = "end"; + break; + default: + case 'n': + anchor = "middle"; + break; + } + + svggen_printf(job, ""); + svggen_fputs(job, xml_string(para->str)); + svggen_fputs(job, "\n"); +} + +static void svggen_ellipse(GVJ_t * job, pointf * A, int filled) +{ + /* A[] contains 2 points: the center and corner. */ + svggen_fputs(job, "\n"); +} + +static void +svggen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, + int arrow_at_end, int filled) +{ + svggen_fputs(job, "\n"); +} + +static void svggen_polygon(GVJ_t * job, pointf * A, int n, int filled) +{ + int i; + + svggen_fputs(job, "\n"); +} + +static void svggen_polyline(GVJ_t * job, pointf * A, int n) +{ + int i; + + svggen_fputs(job, "\n"); +} + +#if 0 +static void +svggen_user_shape(GVJ_t * job, char *name, pointf * A, int n, int filled) +{ + int i; + point p; + pointf pf; + point sz; + char *imagefile; + int minx, miny; + + if (job->style->pen == PEN_NONE) { + /* its invisible, don't draw */ + return; + } + imagefile = agget(gvc->n, "shapefile"); + if (imagefile == 0) { + svggen_polygon(gvc, A, n, filled); + return; + } + pf.x = ND_coord_i(gvc->n).x - ND_lw_i(gvc->n); + pf.y = ND_coord_i(gvc->n).y + ND_ht_i(gvc->n) / 2; + p = svgpt(gvc, pf); + sz.x = ROUND((ND_lw_i(gvc->n) + ND_rw_i(gvc->n))); + sz.y = ROUND(ND_ht_i(gvc->n)); + + svggen_fputs(job, "n->name); + svggen_fputs(job, "\">\n\n\nn->name); + svggen_fputs(job, ")\"/>\n"); +} +#endif + +/* color names from http://www.w3.org/TR/SVG/types.html */ +/* NB. List must be LANG_C sorted */ +static char *svggen_knowncolors[] = { + "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", + "beige", "bisque", "black", "blanchedalmond", "blue", + "blueviolet", "brown", "burlywood", + "cadetblue", "chartreuse", "chocolate", "coral", + "cornflowerblue", "cornsilk", "crimson", "cyan", + "darkblue", "darkcyan", "darkgoldenrod", "darkgray", + "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", + "darkolivegreen", "darkorange", "darkorchid", "darkred", + "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", + "darkslategrey", "darkturquoise", "darkviolet", "deeppink", + "deepskyblue", "dimgray", "dimgrey", "dodgerblue", + "firebrick", "floralwhite", "forestgreen", "fuchsia", + "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", + "green", "greenyellow", "grey", + "honeydew", "hotpink", "indianred", + "indigo", "ivory", "khaki", + "lavender", "lavenderblush", "lawngreen", "lemonchiffon", + "lightblue", "lightcoral", "lightcyan", "lightgoldenrodyellow", + "lightgray", "lightgreen", "lightgrey", "lightpink", + "lightsalmon", "lightseagreen", "lightskyblue", + "lightslategray", "lightslategrey", "lightsteelblue", + "lightyellow", "lime", "limegreen", "linen", + "magenta", "maroon", "mediumaquamarine", "mediumblue", + "mediumorchid", "mediumpurple", "mediumseagreen", + "mediumslateblue", "mediumspringgreen", "mediumturquoise", + "mediumvioletred", "midnightblue", "mintcream", + "mistyrose", "moccasin", + "navajowhite", "navy", "oldlace", + "olive", "olivedrab", "orange", "orangered", "orchid", + "palegoldenrod", "palegreen", "paleturquoise", + "palevioletred", "papayawhip", "peachpuff", "peru", "pink", + "plum", "powderblue", "purple", + "red", "rosybrown", "royalblue", + "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", + "sienna", "silver", "skyblue", "slateblue", "slategray", + "slategrey", "snow", "springgreen", "steelblue", + "tan", "teal", "thistle", "tomato", "turquoise", + "violet", + "wheat", "white", "whitesmoke", + "yellow", "yellowgreen" +}; + +gvrender_engine_t svggen_engine = { + svggen_begin_job, + 0, /* svggen_end_job */ + svggen_begin_graph, + svggen_end_graph, + svggen_begin_layer, + svggen_end_layer, + svggen_begin_page, + svggen_end_page, + svggen_begin_cluster, + svggen_end_cluster, + 0, /* svggen_begin_nodes */ + 0, /* svggen_end_nodes */ + 0, /* svggen_begin_edges */ + 0, /* svggen_end_edges */ + svggen_begin_node, + svggen_end_node, + svggen_begin_edge, + svggen_end_edge, + svggen_begin_anchor, + svggen_end_anchor, + svggen_textpara, + 0, /* svggen_resolve_color */ + svggen_ellipse, + svggen_polygon, + svggen_bezier, + svggen_polyline, + svggen_comment, + 0 /* FIXME svggen_user_shape, */ +}; + +gvrender_features_t svggen_features = { + GVRENDER_DOES_TRUECOLOR + | GVRENDER_Y_GOES_DOWN, /* flags*/ + DEFAULT_EMBED_MARGIN, /* default margin - points */ + {72.,72.}, /* default dpi */ + svggen_knowncolors, /* knowncolors */ + sizeof(svggen_knowncolors) / sizeof(char *), /* sizeof knowncolors */ + RGBA_BYTE, /* color_type */ +}; + +gvplugin_installed_t gvrender_svggen_types[] = { + {FORMAT_SVG, "svg", -1, &svggen_engine, &svggen_features}, +#if HAVE_LIBZ + {FORMAT_SVGZ, "svgz", -1, &svggen_engine, &svggen_features}, +#endif + {0, NULL, 0, NULL, NULL} +}; diff --git a/plugin/gd/gvplugin_gd.c b/plugin/gd/gvplugin_gd.c index b68754b43..9a89b7f47 100644 --- a/plugin/gd/gvplugin_gd.c +++ b/plugin/gd/gvplugin_gd.c @@ -17,12 +17,12 @@ #include "gvplugin.h" extern gvplugin_installed_t gvrender_gd_types; -extern gvplugin_installed_t gvrender_ps_types; +extern gvplugin_installed_t gvrender_gd_ps_types; extern gvplugin_installed_t gvtextlayout_gd_types; static gvplugin_api_t apis[] = { {API_render, &gvrender_gd_types}, - {API_render, &gvrender_ps_types}, + {API_render, &gvrender_gd_ps_types}, {API_textlayout, &gvtextlayout_gd_types}, {(api_t)0, NULL}, }; diff --git a/plugin/pango/gvplugin_pango.c b/plugin/pango/gvplugin_pango.c new file mode 100644 index 000000000..b26640585 --- /dev/null +++ b/plugin/pango/gvplugin_pango.c @@ -0,0 +1,28 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#include "gvplugin.h" + +extern gvplugin_installed_t gvrender_pangogen_types; +extern gvplugin_installed_t gvtextlayout_pangogen_types; + +static gvplugin_api_t apis[] = { + {API_render, &gvrender_pangogen_types}, + {API_textlayout, &gvtextlayout_pangogen_types}, + {(api_t)0, NULL}, +}; + +gvplugin_library_t gvplugin_pango_LTX_library = { "pango", apis }; diff --git a/plugin/pango/gvrender_pango.c b/plugin/pango/gvrender_pango.c new file mode 100644 index 000000000..dba9cb0f2 --- /dev/null +++ b/plugin/pango/gvrender_pango.c @@ -0,0 +1,589 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#include + +#if defined(HAVE_FENV_H) && defined(HAVE_FESETENV) && defined(HAVE_FEGETENV) && defined(HAVE_FEENABLEEXCEPT) + +/* _GNU_SOURCE is needed (supposedly) for the feenableexcept + * prototype to be defined in fenv.h on GNU systems. + * Presumably it will do no harm on other systems. + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +/* We are not supposed to need __USE_GNU, but I can't see + * how to get the prototype for fedisableexcept from + * /usr/include/fenv.h without it. + */ +#ifndef __USE_GNU +#define __USE_GNU +#endif + +# include +#elif HAVE_FPU_CONTROL_H +# include +#elif HAVE_SYS_FPU_H +# include +#endif + +#include "gvplugin_render.h" + +#ifdef HAVE_PANGOCAIRO +#include + +typedef enum { FORMAT_PNG, + FORMAT_PS, + FORMAT_PDF, + FORMAT_SVG, + FORMAT_GTK, + FORMAT_XLIB, + FORMAT_XCB, + FORMAT_SDL, + FORMAT_GLITZ, + FORMAT_QUARTZ, + FORMAT_WIN32, + } format_type; + +#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0])) + +static double dashed[] = {10.}; +static int dashed_len = ARRAY_SIZE(dashed); + +static double dotted[] = {2., 10.}; +static int dotted_len = ARRAY_SIZE(dotted); + +#ifdef CAIRO_HAS_PNG_SURFACE +#include +#endif + +#ifdef CAIRO_HAS_PS_SURFACE +#include +#endif + +#ifdef CAIRO_HAS_PDF_SURFACE +#include +#endif + +#ifdef CAIRO_HAS_SVG_SURFACE +#include +#endif + +#ifdef CAIRO_HAS_XLIB_SURFACE +#include +#endif + +#if defined(HAVE_FENV_H) && defined(HAVE_FESETENV) && defined(HAVE_FEGETENV) && defined(HAVE_FEENABLEEXCEPT) +/* place to save fp environment temporarily */ +static fenv_t fenv; /* FIXME - not thread safe */ +#endif + +static void cairogen_set_color(cairo_t * cr, gvcolor_t * color) +{ + cairo_set_source_rgba(cr, color->u.RGBA[0], color->u.RGBA[1], + color->u.RGBA[2], color->u.RGBA[3]); +} + +static cairo_status_t +writer (void *closure, const unsigned char *data, unsigned int length) +{ + if (length == fwrite(data, 1, length, (FILE *)closure)) + return CAIRO_STATUS_SUCCESS; + return CAIRO_STATUS_WRITE_ERROR; +} + +static cairo_status_t +reader (void *closure, unsigned char *data, unsigned int length) +{ + if (length == fread(data, 1, length, (FILE *)closure) + || feof((FILE *)closure)) + return CAIRO_STATUS_SUCCESS; + return CAIRO_STATUS_READ_ERROR; +} + +static void cairogen_begin_graph(GVJ_t * job, char *graphname) +{ + cairo_t *cr; + cairo_surface_t *surface; + double width, height; + +#if defined(HAVE_FENV_H) && defined(HAVE_FESETENV) && defined(HAVE_FEGETENV) && defined(HAVE_FEDISABLEEXCEPT) + /* cairo generates FE_INVALID and other exceptions we + * want to ignore for now. Save the current fenv and + * set one just for cairo. + * The fenv is restored in cairogen_end_graph */ + fegetenv(&fenv); + fedisableexcept(FE_ALL_EXCEPT); +#endif + + cr = (cairo_t *) job->surface; /* might be NULL */ + + /* device size with margins all around */ + width = job->width + 2 * ROUND(job->margin.x * job->dpi.x / POINTS_PER_INCH); + height = job->height + 2 * ROUND(job->margin.y * job->dpi.y / POINTS_PER_INCH); + + switch (job->render.id) { +#ifdef CAIRO_HAS_PNG_FUNCTIONS + case FORMAT_PNG: + if (!cr) { + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + cr = cairo_create(surface); + cairo_surface_destroy (surface); + } + break; +#endif +#ifdef CAIRO_HAS_PS_SURFACE + case FORMAT_PS: + if (!cr) { + surface = cairo_ps_surface_create_for_stream (writer, + job->output_file, width, height); + cr = cairo_create(surface); + cairo_surface_destroy (surface); + } + break; +#endif +#ifdef CAIRO_HAS_PDF_SURFACE + case FORMAT_PDF: + if (!cr) { + surface = cairo_pdf_surface_create_for_stream (writer, + job->output_file, width, height); + cr = cairo_create(surface); + cairo_surface_destroy (surface); + } + break; +#endif +#ifdef CAIRO_HAS_SVG_SURFACE + case FORMAT_SVG: + if (!cr) { + surface = cairo_svg_surface_create_for_stream (writer, + job->output_file, width, height); + cr = cairo_create(surface); + cairo_surface_destroy (surface); + } + break; +#endif +#ifdef CAIRO_HAS_XLIB_SURFACE + case FORMAT_GTK: + break; + case FORMAT_XLIB: + break; +#endif +#ifdef CAIRO_HAS_XCB_SURFACE + case FORMAT_XCB: + break; +#endif +#ifdef CAIRO_HAS_SDL_SURFACE + case FORMAT_SDL: + break; +#endif +#ifdef CAIRO_HAS_GLITZ_SURFACE + case FORMAT_GLITZ: + break; +#endif + default: + break; + } + job->surface = (void *) cr; +} + +static void cairogen_end_graph(GVJ_t * job) +{ + cairo_t *cr = (cairo_t *) job->surface; + cairo_surface_t *surface; + + switch (job->render.id) { +#ifdef CAIRO_HAS_PNG_FUNCTIONS + case FORMAT_PNG: + surface = cairo_get_target(cr); + cairo_surface_write_to_png_stream(surface, writer, job->output_file); +#endif + default: + break; + } + if (!job->external_surface) + cairo_destroy(cr); + +#if defined HAVE_FENV_H && defined HAVE_FESETENV && defined HAVE_FEGETENV && defined(HAVE_FEENABLEEXCEPT) + /* Restore FP environment */ + fesetenv(&fenv); +#endif +} + +static void cairogen_begin_page(GVJ_t * job) +{ + cairo_t *cr = (cairo_t *) job->surface; + + cairo_save(cr); + cairo_scale(cr, job->zoom * job->dpi.x / POINTS_PER_INCH, + job->zoom * job->dpi.y / POINTS_PER_INCH); + if (job->rotation) { + cairo_rotate(cr, job->rotation * -M_PI / 180.); + cairo_translate(cr, + -job->margin.x / job->zoom - job->pageBox.UR.x, + job->margin.y / job->zoom + job->pageBox.UR.y); + } + else + cairo_translate(cr, + job->margin.x / job->zoom - job->pageBox.LL.x, + job->margin.y / job->zoom + job->pageBox.UR.y); +} + +static void cairogen_end_page(GVJ_t * job) +{ + cairo_t *cr = (cairo_t *) job->surface; + + cairo_restore(cr); + + switch (job->render.id) { +#ifdef CAIRO_HAS_PS_SURFACE + case FORMAT_PS: + cairo_show_page(cr); + break; +#endif +#ifdef CAIRO_HAS_PDF_SURFACE + case FORMAT_PDF: + cairo_show_page(cr); + break; +#endif +#ifdef CAIRO_HAS_SVG_SURFACE + case FORMAT_SVG: + cairo_show_page(cr); + break; +#endif + default: + break; + } +} + +static void cairogen_textpara(GVJ_t * job, pointf p, textpara_t * para) +{ + gvstyle_t *style = job->style; + cairo_t *cr = (cairo_t *) job->surface; + pointf offset; + PangoLayout *layout = (PangoLayout*)(para->layout); + PangoLayoutIter* iter; + + cairo_set_dash (cr, dashed, 0, 0.0); /* clear any dashing */ + cairogen_set_color(cr, &(style->pencolor)); + + switch (para->just) { + case 'r': + offset.x = para->width; + break; + case 'l': + offset.x = 0.0; + break; + case 'n': + default: + offset.x = para->width / 2.0; + break; + } + /* offset to baseline */ + iter = pango_layout_get_iter (layout); + offset.y = pango_layout_iter_get_baseline (iter) / PANGO_SCALE; + + cairo_move_to (cr, p.x-offset.x, -p.y-offset.y); + pango_cairo_show_layout(cr, layout); +} + +static void cairogen_ellipse(GVJ_t * job, pointf * A, int filled) +{ + gvstyle_t *style = job->style; + cairo_t *cr = (cairo_t *) job->surface; + cairo_matrix_t matrix; + double rx, ry; + + if (style->pen == PEN_DASHED) { + cairo_set_dash (cr, dashed, dashed_len, 0.0); + } else if (style->pen == PEN_DOTTED) { + cairo_set_dash (cr, dotted, dotted_len, 0.0); + } else { + cairo_set_dash (cr, dashed, 0, 0.0); + } + cairo_set_line_width (cr, style->penwidth * job->compscale.x); + + cairo_get_matrix(cr, &matrix); + cairo_translate(cr, A[0].x, -A[0].y); + + rx = fabs(A[1].x - A[0].x); + ry = fabs(A[1].y - A[0].y); + cairo_scale(cr, 1, ry / rx); + cairo_move_to(cr, rx, 0); + cairo_arc(cr, 0, 0, rx, 0, 2 * M_PI); + cairo_close_path(cr); + + cairo_set_matrix(cr, &matrix); + + if (filled) { + cairogen_set_color(cr, &(style->fillcolor)); + cairo_fill_preserve(cr); + } + cairogen_set_color(cr, &(style->pencolor)); + cairo_stroke(cr); +} + +static void +cairogen_polygon(GVJ_t * job, pointf * A, int n, int filled) +{ + gvstyle_t *style = job->style; + cairo_t *cr = (cairo_t *) job->surface; + int i; + + if (style->pen == PEN_DASHED) { + cairo_set_dash (cr, dashed, dashed_len, 0.0); + } else if (style->pen == PEN_DOTTED) { + cairo_set_dash (cr, dotted, dotted_len, 0.0); + } else { + cairo_set_dash (cr, dashed, 0, 0.0); + } + cairo_set_line_width (cr, style->penwidth * job->compscale.x); + cairo_move_to(cr, A[0].x, -A[0].y); + for (i = 1; i < n; i++) + cairo_line_to(cr, A[i].x, -A[i].y); + cairo_close_path(cr); + if (filled) { + cairogen_set_color(cr, &(style->fillcolor)); + cairo_fill_preserve(cr); + } + cairogen_set_color(cr, &(style->pencolor)); + cairo_stroke(cr); +} + +static void +cairogen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, + int arrow_at_end, int filled) +{ + gvstyle_t *style = job->style; + cairo_t *cr = (cairo_t *) job->surface; + int i; + + if (style->pen == PEN_DASHED) { + cairo_set_dash (cr, dashed, dashed_len, 0.0); + } else if (style->pen == PEN_DOTTED) { + cairo_set_dash (cr, dotted, dotted_len, 0.0); + } else { + cairo_set_dash (cr, dashed, 0, 0.0); + } + cairo_set_line_width (cr, style->penwidth * job->compscale.x); + cairo_move_to(cr, A[0].x, -A[0].y); + for (i = 1; i < n; i += 3) + cairo_curve_to(cr, A[i].x, -A[i].y, A[i + 1].x, -A[i + 1].y, + A[i + 2].x, -A[i + 2].y); + cairogen_set_color(cr, &(style->pencolor)); + cairo_stroke(cr); +} + +static void +cairogen_polyline(GVJ_t * job, pointf * A, int n) +{ + gvstyle_t *style = job->style; + cairo_t *cr = (cairo_t *) job->surface; + int i; + + if (style->pen == PEN_DASHED) { + cairo_set_dash (cr, dashed, dashed_len, 0.0); + } else if (style->pen == PEN_DOTTED) { + cairo_set_dash (cr, dotted, dotted_len, 0.0); + } else { + cairo_set_dash (cr, dashed, 0, 0.0); + } + cairo_set_line_width (cr, style->penwidth * job->compscale.x); + cairo_move_to(cr, A[0].x, -A[0].y); + for (i = 1; i < n; i++) + cairo_line_to(cr, A[i].x, -A[i].y); + cairogen_set_color(cr, &(style->pencolor)); + cairo_stroke(cr); +} + +static void cairogen_freeimage(void *data) +{ + cairo_destroy((cairo_t*)data); +} + +static void +cairogen_usershape(GVJ_t * job, usershape_t *us, boxf b, bool filled) +{ + cairo_t *cr = (cairo_t *) job->surface; /* target context */ + cairo_surface_t *surface1 = NULL; /* source surface */ + + if (us->data) { + if (us->datafree == cairogen_freeimage) + surface1 = (cairo_surface_t*)(us->data); /* use cached data */ + else { + us->datafree(us->data); /* free incompatible cache data */ + us->data = NULL; + } + } + if (!surface1) { /* read file into cache */ + fseek(us->f, 0, SEEK_SET); + switch (us->type) { +#ifdef CAIRO_HAS_PNG_FUNCTIONS + case FT_PNG: + surface1 = cairo_image_surface_create_from_png_stream(reader, us->f); + cairo_surface_reference(surface1); + break; +#endif + default: + surface1 = NULL; + } + if (surface1) { + us->data = (void*)surface1; + us->datafree = cairogen_freeimage; + } + } + if (surface1) { + cairo_save(cr); + cairo_translate(cr, ROUND(b.LL.x), ROUND(-b.UR.y)); + cairo_scale(cr, (b.UR.x - b.LL.x) / us->w, + (b.UR.y - b.LL.y) / us->h); + cairo_set_source_surface (cr, surface1, 0, 0); + cairo_paint (cr); + cairo_restore(cr); + } +} + +static gvrender_engine_t cairogen_engine = { + 0, /* cairogen_begin_job */ + 0, /* cairogen_end_job */ + cairogen_begin_graph, + cairogen_end_graph, + 0, /* cairogen_begin_layer */ + 0, /* cairogen_end_layer */ + cairogen_begin_page, + cairogen_end_page, + 0, /* cairogen_begin_cluster */ + 0, /* cairogen_end_cluster */ + 0, /* cairogen_begin_nodes */ + 0, /* cairogen_end_nodes */ + 0, /* cairogen_begin_edges */ + 0, /* cairogen_end_edges */ + 0, /* cairogen_begin_node */ + 0, /* cairogen_end_node */ + 0, /* cairogen_begin_edge */ + 0, /* cairogen_end_edge */ + 0, /* cairogen_begin_anchor */ + 0, /* cairogen_end_anchor */ + cairogen_textpara, + 0, /* cairogen_resolve_color */ + cairogen_ellipse, + cairogen_polygon, + cairogen_bezier, + cairogen_polyline, + 0, /* cairogen_comment */ + cairogen_usershape +}; + +static gvrender_features_t cairogen_features = { + GVRENDER_DOES_TRUECOLOR + | GVRENDER_DOES_TRANSFORM, /* flags */ + 0, /* default margin - points */ + {96.,96.}, /* default dpi */ + 0, /* knowncolors */ + 0, /* sizeof knowncolors */ + RGBA_DOUBLE, /* color_type */ + 0, /* device */ +}; + +static gvrender_features_t cairogen_features_ps = { + GVRENDER_DOES_TRUECOLOR + | GVRENDER_DOES_TRANSFORM, /* flags */ + 36, /* default margin - points */ + {72.,72.}, /* postscript 72 dpi */ + 0, /* knowncolors */ + 0, /* sizeof knowncolors */ + RGBA_DOUBLE, /* color_type */ + 0, /* device */ +}; + +#if 0 +static gvrender_features_t cairogen_features_x = { + GVRENDER_DOES_TRUECOLOR + | GVRENDER_DOES_TRANSFORM + | GVRENDER_X11_EVENTS, /* flags */ + 0, /* default margin - points */ + {96.,96.}, /* default dpi */ + 0, /* knowncolors */ + 0, /* sizeof knowncolors */ + RGBA_DOUBLE, /* color_type */ + "xlib", /* device */ +}; + +static gvrender_features_t cairogen_features_gtk = { + GVRENDER_DOES_TRUECOLOR + | GVRENDER_DOES_TRANSFORM + | GVRENDER_X11_EVENTS, /* flags */ + 0, /* default margin - points */ + {96.,96.}, /* default dpi */ + 0, /* knowncolors */ + 0, /* sizeof knowncolors */ + RGBA_DOUBLE, /* color_type */ + "gtk", /* device */ +}; +#endif +#endif + +gvplugin_installed_t gvrender_pangogen_types[] = { +#ifdef HAVE_PANGOCAIRO +#ifdef CAIRO_HAS_PNG_FUNCTIONS + {FORMAT_PNG, "png", 10, &cairogen_engine, &cairogen_features}, +#endif +#ifdef CAIRO_HAS_PS_SURFACE + {FORMAT_PS, "ps", -10, &cairogen_engine, &cairogen_features_ps}, +#endif +#ifdef CAIRO_HAS_PDF_SURFACE + {FORMAT_PDF, "pdf", 0, &cairogen_engine, &cairogen_features_ps}, +#endif +#ifdef CAIRO_HAS_SVG_SURFACE + {FORMAT_SVG, "svg", -10, &cairogen_engine, &cairogen_features_ps}, +#endif +#if 0 +#ifdef CAIRO_HAS_XLIB_SURFACE + {FORMAT_GTK, "gtk", 0, &cairogen_engine, &cairogen_features_gtk}, + {FORMAT_XLIB, "xlib", 0, &cairogen_engine, &cairogen_features_x}, +#endif +#ifdef CAIRO_HAS_XCB_SURFACE + {FORMAT_XCB, "xcb", 0, &cairogen_engine, &cairogen_features_x}, +#endif +#ifdef CAIRO_HAS_SDL_SURFACE + {FORMAT_SDL, "sdl", 0, &cairogen_engine, &cairogen_features_x}, +#endif +#ifdef CAIRO_HAS_GLITZ_SURFACE + {FORMAT_GLITZ, "glitz", 0, &cairogen_engine, &cairogen_features_x}, +#endif +#ifdef CAIRO_HAS_QUARTZ_SURFACE + {FORMAT_QUARTZ, "quartz", 0, &cairogen_engine, &cairogen_features_x}, +#endif +#ifdef CAIRO_HAS_WIN32_SURFACE + {FORMAT_WIN32, "win32", 0, &cairogen_engine, &cairogen_features_x}, +#endif +#endif +#endif + {0, NULL, 0, NULL, NULL} +}; diff --git a/plugin/pango/gvtextlayout_pango.c b/plugin/pango/gvtextlayout_pango.c new file mode 100644 index 000000000..04fcf7410 --- /dev/null +++ b/plugin/pango/gvtextlayout_pango.c @@ -0,0 +1,108 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gvplugin_textlayout.h" + +#ifdef HAVE_PANGOCAIRO +#include + +static void pangocairo_free_layout (void *layout) +{ + g_object_unref((PangoLayout*)layout); +} + +static void pangocairo_textlayout(textpara_t * para, char **fontpath) +{ + static PangoFontMap *fontmap; + static PangoContext *context; + PangoFontDescription *desc; + PangoLayout *layout; + PangoRectangle ink_rect, logical_rect; +#if ENABLE_PANGO_XSHOW + PangoRectangle char_rect; + PangoLayoutIter* iter; +#endif +#ifdef ENABLE_PANGO_MARKUP + PangoAttrList *attrs; + GError *error = NULL; +#endif + char *text; + + if (!fontmap) + fontmap = pango_cairo_font_map_get_default(); + pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(fontmap), (double)POINTS_PER_INCH); + if (!context) + context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP(fontmap)); + + desc = pango_font_description_new(); + pango_font_description_set_family (desc, para->fontname); + pango_font_description_set_size (desc, (gint)(para->fontsize * PANGO_SCALE)); + +#ifdef ENABLE_PANGO_MARKUP + if (! pango_parse_markup (para->str, -1, 0, &attrs, &text, NULL, &error)) + die(error->message); +#else + text = para->str; +#endif + + layout = pango_layout_new (context); + para->layout = (void *)layout; /* layout free with textpara - see labels.c */ + para->free_layout = pangocairo_free_layout; /* function for freeing pango layout */ + + pango_layout_set_text (layout, text, -1); + pango_layout_set_font_description (layout, desc); +#ifdef ENABLE_PANGO_MARKUP + pango_layout_set_attributes (layout, attrs); +#endif + + pango_layout_get_extents (layout, &ink_rect, &logical_rect); + para->width = (double)logical_rect.width / PANGO_SCALE; + para->height = (double)logical_rect.height / PANGO_SCALE; + + /* determine position of each character in the layout */ + para->xshow = NULL; +#ifdef ENABLE_PANGO_XSHOW +/* FIXME - unfinished code */ + iter = pango_layout_get_iter (layout); + do { + pango_layout_iter_get_char_extents (iter, &char_rect); + char_rect.x /= PANGO_SCALE; + char_rect.y /= PANGO_SCALE; + } while (pango_layout_iter_next_char (iter)); + pango_layout_iter_free (iter); +#endif + + pango_font_description_free (desc); + + *fontpath = "[pango]"; +} + +static gvtextlayout_engine_t pangocairo_textlayout_engine = { + pangocairo_textlayout, +}; +#endif + +gvplugin_installed_t gvtextlayout_pangocairogen_types[] = { +#ifdef HAVE_PANGOCAIRO + {0, "textlayout", 10, &pangocairo_textlayout_engine, NULL}, +#endif + {0, NULL, 0, NULL, NULL} +}; -- 2.40.0