From 7d24854ab230cd5ce1d39e0cea0ff54f55aebf72 Mon Sep 17 00:00:00 2001 From: ellson Date: Tue, 2 Sep 2008 14:56:42 +0000 Subject: [PATCH] Add framework for a new lasi plugin, currently disabled. The lasi library provides UTF-8 support in PostScript --- configure.ac | 24 ++ plugin/Makefile.am | 2 +- plugin/lasi/.cvsignore | 7 + plugin/lasi/Makefile.am | 42 +++ plugin/lasi/gvrender_lasi.c | 500 ++++++++++++++++++++++++++++++++++++ 5 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 plugin/lasi/.cvsignore create mode 100644 plugin/lasi/Makefile.am create mode 100644 plugin/lasi/gvrender_lasi.c diff --git a/configure.ac b/configure.ac index d34a7c142..4f017b60c 100644 --- a/configure.ac +++ b/configure.ac @@ -1865,6 +1865,28 @@ if test "x$use_pangocairo" = "xYes"; then LIBS=$save_LIBS fi +dnl ----------------------------------- +dnl INCLUDES and LIBS for LASI + +AC_ARG_WITH(lasi, + [AC_HELP_STRING([--with-lasi=no], [lasi library])], + [], [with_lasi=no]) + +if test "x$with_lasi" != "xyes"; then + use_lasi="No (disabled by default - incomplete)" +else + PKG_CHECK_MODULES(LASI, [lasi],[ + use_lasi="Yes" + AC_DEFINE_UNQUOTED(HAVE_LASI,1, + [Define if you have the lasi library]) + AC_SUBST(LASI_CFLAGS) + AC_SUBST(LASI_LIBS) + ],[ + use_lasi="No (lasi library not available)" + ]) +fi +AM_CONDITIONAL(WITH_LASI, [test "x$use_lasi" = "xYes"]) + dnl ----------------------------------- dnl INCLUDES and LIBS for GLITZ @@ -2871,6 +2893,7 @@ AC_CONFIG_FILES(Makefile plugin/gdiplus/Makefile plugin/glitz/Makefile plugin/gtk/Makefile + plugin/lasi/Makefile plugin/ming/Makefile plugin/pango/Makefile plugin/quartz/Makefile @@ -2987,6 +3010,7 @@ echo " gdiplus: $use_gdiplus" echo " gdk_pixbuf: $use_gdk_pixbuf" echo " glitz: $use_glitz" echo " gtk: $use_gtk" +echo " lasi: $use_lasi" echo " ming: $use_ming" echo " pangocairo: $use_pangocairo" echo " quartz: $use_quartz" diff --git a/plugin/Makefile.am b/plugin/Makefile.am index e1f3ec2a7..46b664d23 100644 --- a/plugin/Makefile.am +++ b/plugin/Makefile.am @@ -1,6 +1,6 @@ # $Id$ $Revision$ ## Process this file with automake to produce Makefile.in -SUBDIRS = core devil gd gdk_pixbuf gdiplus glitz gtk ming pango quartz rsvg xlib dot_layout neato_layout +SUBDIRS = core devil gd gdk_pixbuf gdiplus glitz gtk lasi ming pango quartz rsvg xlib dot_layout neato_layout EXTRA_DIST = Makefile.old diff --git a/plugin/lasi/.cvsignore b/plugin/lasi/.cvsignore new file mode 100644 index 000000000..2ae581798 --- /dev/null +++ b/plugin/lasi/.cvsignore @@ -0,0 +1,7 @@ +*.la +*.lo +.deps +.libs +Makefile +Makefile.in +CMakeFiles CMakeTmp CMakeFiles CMakeCache.txt cmake_install.cmake diff --git a/plugin/lasi/Makefile.am b/plugin/lasi/Makefile.am new file mode 100644 index 000000000..291935f36 --- /dev/null +++ b/plugin/lasi/Makefile.am @@ -0,0 +1,42 @@ +# $Id$ $Revision$ +## Process this file with automake to produce Makefile.in + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/lib/common \ + -I$(top_srcdir)/lib/pathplan \ + -I$(top_srcdir)/lib/gvc \ + -I$(top_srcdir)/lib/graph \ + -I$(top_srcdir)/lib/cdt \ + $(LASI_CFLAGS) $(PANGOCAIRO_CFLAGS) $(FREETYPE2_CFLAGS) $(LIBGVC_CFLAGS) + +if WITH_LASI +if WITH_PANGOCAIRO +noinst_LTLIBRARIES = libgvplugin_lasi_C.la +if WITH_WIN32 +lib_LTLIBRARIES = libgvplugin_lasi.la +else +pkglib_LTLIBRARIES = libgvplugin_lasi.la +endif +endif +endif + +libgvplugin_lasi_C_la_SOURCES = \ + gvplugin_lasi.c \ + gvrender_lasi.c + +libgvplugin_lasi_la_LDFLAGS = -version-info @GVPLUGIN_VERSION_INFO@ +libgvplugin_lasi_la_SOURCES = $(libgvplugin_lasi_C_la_SOURCES) +libgvplugin_lasi_la_LIBADD = \ + $(top_builddir)/lib/gvc/libgvc.la @LASI_LIBS@ @PANGOCAIRO_LIBS@ @MATH_LIBS@ + +if WITH_WIN32 +libgvplugin_lasi_la_LDFLAGS += -no-undefined +endif + +if WITH_DARWIN9 +libgvplugin_lasi_la_LDFLAGS += -Wl,-exported_symbol,_gvplugin_lasi_LTX_library +endif + +EXTRA_DIST = Makefile.old + diff --git a/plugin/lasi/gvrender_lasi.c b/plugin/lasi/gvrender_lasi.c new file mode 100644 index 000000000..282e0b9a4 --- /dev/null +++ b/plugin/lasi/gvrender_lasi.c @@ -0,0 +1,500 @@ +/* $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 "gvplugin_render.h" +#include "graph.h" +#include "agxbuf.h" +#include "utils.h" + +/* for CHAR_LATIN1 */ +#include "const.h" + +/* + * J$: added `pdfmark' URL embedding. PostScript rendered from + * dot files with URL attributes will get active PDF links + * from Adobe's Distiller. + */ +#define PDFMAX 14400 /* Maximum size of PDF page */ + +extern void epsf_define(FILE * of); +extern char *ps_string(char *ins, int latin); + +typedef enum { FORMAT_PS, FORMAT_PS2, FORMAT_EPS } format_type; + +static int isLatin1; +static char setupLatin1; + +static void psgen_begin_job(GVJ_t * job) +{ + gvdevice_fputs(job, "%!PS-Adobe-3.0 EPSF-3.0\n"); + gvdevice_printf(job, "%%%%Creator: %s version %s (%s)\n", + job->common->info[0], job->common->info[1], job->common->info[2]); + gvdevice_printf(job, "%%%%For: %s\n", job->common->user); +} + +static void psgen_end_job(GVJ_t * job) +{ + gvdevice_fputs(job, "%%Trailer\n"); + if (job->render.id != FORMAT_EPS) + gvdevice_printf(job, "%%%%Pages: %d\n", job->common->viewNum); + if (job->common->show_boxes == NULL) + if (job->render.id != FORMAT_EPS) + gvdevice_printf(job, "%%%%BoundingBox: %d %d %d %d\n", + job->boundingBox.LL.x, job->boundingBox.LL.y, + job->boundingBox.UR.x, job->boundingBox.UR.y); + gvdevice_fputs(job, "end\nrestore\n"); + gvdevice_fputs(job, "%%EOF\n"); +} + +static void psgen_begin_graph(GVJ_t * job) +{ + obj_state_t *obj = job->obj; + + setupLatin1 = FALSE; + + if (job->common->viewNum == 0) { + gvdevice_printf(job, "%%%%Title: %s\n", obj->u.g->name); + if (job->render.id != FORMAT_EPS) + gvdevice_fputs(job, "%%Pages: (atend)\n"); + else + gvdevice_fputs(job, "%%Pages: 1\n"); + if (job->common->show_boxes == NULL) { + if (job->render.id != FORMAT_EPS) + gvdevice_fputs(job, "%%BoundingBox: (atend)\n"); + else + gvdevice_printf(job, "%%%%BoundingBox: %d %d %d %d\n", + job->pageBoundingBox.LL.x, job->pageBoundingBox.LL.y, + job->pageBoundingBox.UR.x, job->pageBoundingBox.UR.y); + } + gvdevice_fputs(job, "%%EndComments\nsave\n"); + /* include shape library */ + cat_preamble(job, job->common->lib); + /* include epsf */ + epsf_define(job->output_file); + if (job->common->show_boxes) { + char* args[2]; + args[0] = job->common->show_boxes[0]; + args[1] = NULL; + cat_libfile(job->output_file, NULL, args); + } + } + isLatin1 = (GD_charset(obj->u.g) == CHAR_LATIN1); + /* We always setup Latin1. The charset info is always output, + * and installing it is cheap. With it installed, we can then + * rely on ps_string to convert UTF-8 characters whose encoding + * is in the range of Latin-1 into the Latin-1 equivalent and + * get the expected PostScript output. + */ + if (!setupLatin1) { + gvdevice_fputs(job, "setupLatin1\n"); /* as defined in ps header */ + setupLatin1 = TRUE; + } + /* Set base URL for relative links (for Distiller >= 3.0) */ + if (obj->url) + gvdevice_printf(job, "[ {Catalog} << /URI << /Base (%s) >> >>\n" + "/PUT pdfmark\n", obj->url); +} + +static void psgen_begin_layer(GVJ_t * job, char *layername, int layerNum, int numLayers) +{ + gvdevice_printf(job, "%d %d setlayer\n", layerNum, numLayers); +} + +static void psgen_begin_page(GVJ_t * job) +{ + box pbr = job->pageBoundingBox; + + gvdevice_printf(job, "%%%%Page: %d %d\n", + job->common->viewNum + 1, job->common->viewNum + 1); + if (job->common->show_boxes == NULL) + gvdevice_printf(job, "%%%%PageBoundingBox: %d %d %d %d\n", + pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y); + gvdevice_printf(job, "%%%%PageOrientation: %s\n", + (job->rotation ? "Landscape" : "Portrait")); + if (job->render.id == FORMAT_PS2) + gvdevice_printf(job, "<< /PageSize [%d %d] >> setpagedevice\n", + pbr.UR.x, pbr.UR.y); + gvdevice_printf(job, "%d %d %d beginpage\n", + job->pagesArrayElem.x, job->pagesArrayElem.y, job->numPages); + if (job->common->show_boxes == NULL) + gvdevice_printf(job, "gsave\n%d %d %d %d boxprim clip newpath\n", + pbr.LL.x, pbr.LL.y, pbr.UR.x-pbr.LL.x, pbr.UR.y-pbr.LL.y); + gvdevice_printf(job, "%g %g set_scale %d rotate %g %g translate\n", + job->scale.x, job->scale.y, + job->rotation, + job->translation.x, job->translation.y); + + /* Define the size of the PS canvas */ + if (job->render.id == FORMAT_PS2) { + if (pbr.UR.x >= PDFMAX || pbr.UR.y >= PDFMAX) + job->common->errorfn("canvas size (%d,%d) exceeds PDF limit (%d)\n" + "\t(suggest setting a bounding box size, see dot(1))\n", + pbr.UR.x, pbr.UR.y, PDFMAX); + gvdevice_printf(job, "[ /CropBox [%d %d %d %d] /PAGES pdfmark\n", + pbr.LL.x, pbr.LL.y, pbr.UR.x, pbr.UR.y); + } +} + +static void psgen_end_page(GVJ_t * job) +{ + if (job->common->show_boxes) { + gvdevice_fputs(job, "0 0 0 edgecolor\n"); + 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. + */ + gvdevice_fputs(job, "endpage\nshowpage\ngrestore\n"); + gvdevice_fputs(job, "%%PageTrailer\n"); + gvdevice_printf(job, "%%%%EndPage: %d\n", job->common->viewNum); +} + +static void psgen_begin_cluster(GVJ_t * job) +{ + obj_state_t *obj = job->obj; + + gvdevice_printf(job, "%% %s\n", obj->u.sg->name); + + gvdevice_fputs(job, "gsave\n"); +} + +static void psgen_end_cluster(GVJ_t * job) +{ + gvdevice_fputs(job, "grestore\n"); +} + +static void psgen_begin_node(GVJ_t * job) +{ + gvdevice_fputs(job, "gsave\n"); +} + +static void psgen_end_node(GVJ_t * job) +{ + gvdevice_fputs(job, "grestore\n"); +} + +static void +psgen_begin_edge(GVJ_t * job) +{ + gvdevice_fputs(job, "gsave\n"); +} + +static void psgen_end_edge(GVJ_t * job) +{ + gvdevice_fputs(job, "grestore\n"); +} + +static void psgen_begin_anchor(GVJ_t *job, char *url, char *tooltip, char *target) +{ + obj_state_t *obj = job->obj; + + if (url && obj->url_map_p) { + gvdevice_fputs(job, "[ /Rect [ "); + gvdevice_printpointflist(job, obj->url_map_p, 2); + gvdevice_fputs(job, " ]\n"); + gvdevice_printf(job, " /Border [ 0 0 0 ]\n" + " /Action << /Subtype /URI /URI %s >>\n" + " /Subtype /Link\n" + "/ANN pdfmark\n", + ps_string(url, isLatin1)); + } +} + +static void +ps_set_pen_style(GVJ_t *job) +{ + double penwidth = job->obj->penwidth; + char *p, *line, **s = job->obj->rawstyle; + + gvdevice_printnum(job, penwidth); + gvdevice_fputs(job," setlinewidth\n"); + + while (s && (p = line = *s++)) { + if (strcmp(line, "setlinewidth") == 0) + continue; + while (*p) + p++; + p++; + while (*p) { + gvdevice_printf(job,"%s ", p); + while (*p) + p++; + p++; + } + if (strcmp(line, "invis") == 0) + job->obj->penwidth = 0; + gvdevice_printf(job, "%s\n", line); + } +} + +static void ps_set_color(GVJ_t *job, gvcolor_t *color) +{ + char *objtype; + + if (color) { + switch (job->obj->type) { + case ROOTGRAPH_OBJTYPE: + case CLUSTER_OBJTYPE: + objtype = "graph"; + break; + case NODE_OBJTYPE: + objtype = "node"; + break; + case EDGE_OBJTYPE: + objtype = "edge"; + break; + default: + objtype = "sethsb"; + break; + } + gvdevice_printf(job, "%.3f %.3f %.3f %scolor\n", + color->u.HSVA[0], color->u.HSVA[1], color->u.HSVA[2], objtype); + } +} + +static void psgen_textpara(GVJ_t * job, pointf p, textpara_t * para) +{ + char *str; + + if (job->obj->pencolor.u.HSVA[3] < .5) + return; /* skip transparent text */ + + ps_set_color(job, &(job->obj->pencolor)); + gvdevice_printnum(job, para->fontsize); + gvdevice_printf(job, " /%s set_font\n", para->fontname); + str = ps_string(para->str,isLatin1); + switch (para->just) { + case 'r': + p.x -= para->width; + break; + case 'l': + p.x -= 0.0; + break; + case 'n': + default: + p.x -= para->width / 2.0; + break; + } + p.y += para->yoffset_centerline; + gvdevice_printpointf(job, p); + gvdevice_fputs(job, " moveto "); + gvdevice_printnum(job, para->width); + gvdevice_printf(job, " %s alignedtext\n", str); +} + +static void psgen_ellipse(GVJ_t * job, pointf * A, int filled) +{ + /* A[] contains 2 points: the center and corner. */ + pointf AA[2]; + + AA[0] = A[0]; + AA[1].x = A[1].x - A[0].x; + AA[1].y = A[1].y - A[0].y; + + if (filled && job->obj->fillcolor.u.HSVA[3] > .5) { + ps_set_color(job, &(job->obj->fillcolor)); + gvdevice_printpointflist(job, AA, 2); + gvdevice_fputs(job, " ellipse_path fill\n"); + } + if (job->obj->pencolor.u.HSVA[3] > .5) { + ps_set_pen_style(job); + ps_set_color(job, &(job->obj->pencolor)); + gvdevice_printpointflist(job, AA, 2); + gvdevice_fputs(job, " ellipse_path stroke\n"); + } +} + +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 && job->obj->fillcolor.u.HSVA[3] > .5) { + ps_set_color(job, &(job->obj->fillcolor)); + gvdevice_fputs(job, "newpath "); + gvdevice_printpointf(job, A[0]); + gvdevice_fputs(job, " moveto\n"); + for (j = 1; j < n; j += 3) { + gvdevice_printpointflist(job, &A[j], 3); + gvdevice_fputs(job, " curveto\n"); + } + gvdevice_fputs(job, "closepath fill\n"); + } + if (job->obj->pencolor.u.HSVA[3] > .5) { + ps_set_pen_style(job); + ps_set_color(job, &(job->obj->pencolor)); + gvdevice_fputs(job, "newpath "); + gvdevice_printpointf(job, A[0]); + gvdevice_fputs(job, " moveto\n"); + for (j = 1; j < n; j += 3) { + gvdevice_printpointflist(job, &A[j], 3); + gvdevice_fputs(job, " curveto\n"); + } + gvdevice_fputs(job, "stroke\n"); + } +} + +static void psgen_polygon(GVJ_t * job, pointf * A, int n, int filled) +{ + int j; + + if (filled && job->obj->fillcolor.u.HSVA[3] > .5) { + ps_set_color(job, &(job->obj->fillcolor)); + gvdevice_fputs(job, "newpath "); + gvdevice_printpointf(job, A[0]); + gvdevice_fputs(job, " moveto\n"); + for (j = 1; j < n; j++) { + gvdevice_printpointf(job, A[j]); + gvdevice_fputs(job, " lineto\n"); + } + gvdevice_fputs(job, "closepath fill\n"); + } + if (job->obj->pencolor.u.HSVA[3] > .5) { + ps_set_pen_style(job); + ps_set_color(job, &(job->obj->pencolor)); + gvdevice_fputs(job, "newpath "); + gvdevice_printpointf(job, A[0]); + gvdevice_fputs(job, " moveto\n"); + for (j = 1; j < n; j++) { + gvdevice_printpointf(job, A[j]); + gvdevice_fputs(job, " lineto\n"); + } + gvdevice_fputs(job, "closepath stroke\n"); + } +} + +static void psgen_polyline(GVJ_t * job, pointf * A, int n) +{ + int j; + + if (job->obj->pencolor.u.HSVA[3] > .5) { + ps_set_pen_style(job); + ps_set_color(job, &(job->obj->pencolor)); + gvdevice_fputs(job, "newpath "); + gvdevice_printpointf(job, A[0]); + gvdevice_fputs(job, " moveto\n"); + for (j = 1; j < n; j++) { + gvdevice_printpointf(job, A[j]); + gvdevice_fputs(job, " lineto\n"); + } + gvdevice_fputs(job, "stroke\n"); + } +} + +static void psgen_comment(GVJ_t * job, char *str) +{ + gvdevice_fputs(job, "% "); + gvdevice_fputs(job, str); + gvdevice_fputs(job, "\n"); +} + +static void psgen_library_shape(GVJ_t * job, char *name, pointf * A, int n, int filled) +{ + if (filled && job->obj->fillcolor.u.HSVA[3] > .5) { + ps_set_color(job, &(job->obj->fillcolor)); + gvdevice_fputs(job, "[ "); + gvdevice_printpointflist(job, A, n); + gvdevice_fputs(job, " "); + gvdevice_printpointf(job, A[0]); + gvdevice_printf(job, " ] %d true %s\n", n, name); + } + if (job->obj->pencolor.u.HSVA[3] > .5) { + ps_set_pen_style(job); + ps_set_color(job, &(job->obj->pencolor)); + gvdevice_fputs(job, "[ "); + gvdevice_printpointflist(job, A, n); + gvdevice_fputs(job, " "); + gvdevice_printpointf(job, A[0]); + gvdevice_printf(job, " ] %d false %s\n", n, name); + } +} + +static gvrender_engine_t psgen_engine = { + psgen_begin_job, + psgen_end_job, + psgen_begin_graph, + 0, /* psgen_end_graph */ + psgen_begin_layer, + 0, /* psgen_end_layer */ + psgen_begin_page, + psgen_end_page, + psgen_begin_cluster, + psgen_end_cluster, + 0, /* psgen_begin_nodes */ + 0, /* psgen_end_nodes */ + 0, /* psgen_begin_edges */ + 0, /* psgen_end_edges */ + psgen_begin_node, + psgen_end_node, + psgen_begin_edge, + psgen_end_edge, + psgen_begin_anchor, + 0, /* psgen_end_anchor */ + psgen_textpara, + 0, /* psgen_resolve_color */ + psgen_ellipse, + psgen_polygon, + psgen_bezier, + psgen_polyline, + psgen_comment, + psgen_library_shape, +}; + +static gvrender_features_t render_features_ps = { + GVRENDER_DOES_TRANSFORM + | GVRENDER_DOES_MAPS + | GVRENDER_NO_BG + | GVRENDER_DOES_MAP_RECTANGLE, + 4., /* default pad - graph units */ + NULL, /* knowncolors */ + 0, /* sizeof knowncolors */ + HSVA_DOUBLE, /* color_type */ +}; + +static gvdevice_features_t device_features_ps = { + GVDEVICE_DOES_PAGES + | GVDEVICE_DOES_LAYERS, /* flags */ + {36.,36.}, /* default margin - points */ + {612.,792.}, /* default page width, height - points */ + {72.,72.}, /* default dpi */ +}; + +static gvdevice_features_t device_features_eps = { + 0, /* flags */ + {36.,36.}, /* default margin - points */ + {612.,792.}, /* default page width, height - points */ + {72.,72.}, /* default dpi */ +}; + +gvplugin_installed_t gvrender_ps_types[] = { + {FORMAT_PS, "ps", 1, &psgen_engine, &render_features_ps}, + {0, NULL, 0, NULL, NULL} +}; + +gvplugin_installed_t gvdevice_ps_types[] = { + {FORMAT_PS, "ps:ps", 1, NULL, &device_features_ps}, + {FORMAT_PS2, "ps2:ps", 1, NULL, &device_features_ps}, + {FORMAT_EPS, "eps:ps", 1, NULL, &device_features_eps}, + {0, NULL, 0, NULL, NULL} +}; -- 2.40.0