]> granicus.if.org Git - graphviz/commitdiff
Add framework for a new lasi plugin, currently disabled.
authorellson <devnull@localhost>
Tue, 2 Sep 2008 14:56:42 +0000 (14:56 +0000)
committerellson <devnull@localhost>
Tue, 2 Sep 2008 14:56:42 +0000 (14:56 +0000)
The lasi library provides UTF-8 support in PostScript

configure.ac
plugin/Makefile.am
plugin/lasi/.cvsignore [new file with mode: 0644]
plugin/lasi/Makefile.am [new file with mode: 0644]
plugin/lasi/gvrender_lasi.c [new file with mode: 0644]

index d34a7c14210e5054690cb62b79f2600caaa091e4..4f017b60c80776dffcdd5a9cdf940dd4806c055d 100644 (file)
@@ -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"
index e1f3ec2a7873a2e40b7556286d88657558c77577..46b664d232adaa9f901ea45ddc4e25c81c616447 100644 (file)
@@ -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 (file)
index 0000000..2ae5817
--- /dev/null
@@ -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 (file)
index 0000000..291935f
--- /dev/null
@@ -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 (file)
index 0000000..282e0b9
--- /dev/null
@@ -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 <stdlib.h>
+#include <string.h>
+
+#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}
+};