# Graphviz Plugins

## Introduction

Graphviz plugins are a means of extending the capabilities of the graphviz tools: `dot`, `neato`, `fdp`, `twopi`, and `circo`,

The initial motivation for the development of plugins was to support alternative renders based on `cairo` , while keeping the stable renderer based on `gd` available. The `gd` renderer does not support anti-aliased lines (first image). Simply by installing the graphviz-cairo package the -Tpng renderer is substituted with one based on cairo (second image).

Use of the newly installed plugin requires no change in the user's command. These images were generated before and after installing `graphviz-cairo`with the command:

    echo "digraph G {hello->world}" | dot -Tpng > hello.png

A second motivation was to support GUI interfaces to graphs, but without bloating the basic version of `dot` with dependencies on `gtk` or `xlib`. The command:

    echo "digraph G {hello->world}" | dot -Txlib

demand loads the xlib renderer from the graphviz-cairo plugins and produces a zoomable display of the graph in a window:

## Objectives and Constraints

- Support independent compilation so that plugins can be provided by others separate from the base graphviz package.
- Support independent packaging so that plugins can be installed at the user's discretion, separate from the base graphviz package.
- Support optional platform-specific functionality without adding complexity to the base package.
- Support on-demand loading of plugins so that extra capabilities do not degrade performance for basic usage of the tools.
- Unbundle some existing functionality into plugins so that the base version of dot can be faster when that functionality is not needed.
- Minimize performance cost of plugin support, particularly if no plugins are demanded.
- Must be able to configure dot with a fixed set of plugins as builtins that are unconditionally loaded at program invocation (linked at load time).
- Must be able to configure dot as a static binary (linked at compile time), including builtins if specified.
- Must support old-style codegens at least until completely replaced by plugins that are equal or better in quality.

## Structure and terminology

A plugin-package, such as `graphviz-cairo-2.6-1.i386.rpm`, provides one or more plugin-libraries.

A plugin-library provides plugins that implement various plugin-apis. The plugin-library is the unit that is dynamically loaded.

There are currently five plugin-apis: layout, textlayout, usershape, render, device.

A plugin-library provides plugin-types which implement plugin-api. e.g. png ps pdf plugin-types implementing the renderer api.

Each plugin-type implementation provides a quality indicator to control the default selection when there are multiple implementations of the same plugin-type. The builtin implementations (if any) have a quality of 0. A positive quality will choose the plugin in preference to the builtin. A negative quality will leave the builtin as the default.

The plugins are self-describing. At installation time, `dot -c` is run to generate `/usr/lib/graphviz/config` which describes the capabilities of the available plugin-libraries so that they don't have to be loaded at startup and can be loaded later on demand.

## The graphviz-cairo package.

The graphviz-cairo package provides multiple plugin-libraries, all with some dependence on libcairo.so. In general, a plugin-package can be used to localize all knowledge and dependencies on some facility like cairo or gtk or quartz. The graphviz-cairo package alongside the graphviz base package is illustrated diagrammatically here:

Once graphviz-cairo is installed, and `dot -c` has been run as part of the installation process, the file: `/usr/lib/graphviz/config` contains:

    libgvplugin_gtk.so.2 gtk {
            device {
                    gtk 0
            }
    }
    libgvplugin_xlib.so.2 xlib {
            device {
                    xlib 0
            }
    }
    libgvplugin_cairo.so.2 cairo {
            render {
                    png 10
                    ps -10
                    pdf 0
                    gtk 0
                    xlib 0
            }
            textlayout {
                    cairo 10
            }
    }
    libgvplugin_dot_layout.so.2 dot_layout {
            layout {
                    dot 0
            }
    }
    libgvplugin_neato_layout.so.2 neato_layout {
            layout {
                    neato 0
                    fdp 0
                    twopi 0
                    circo 0
            }
    }

## Running dot with plugins

Dot attempts to be transparent to the user about its use of plugins. By default it will always chose the highest quality plugin of the matching plugin-type. Sometimes it is necessary to override the default. This can be done by explicitly specifying the package to take the plugin from. e.g. `dot -Tpng` takes the highest quality plugin currently available. `dot -Tpng:cg` takes the png renderer from the codegens.

If the package is unrecognized, the dot will return the set of available packages:

    $ dot -Tpng: hello.dot
    Renderer type: "png:" not recognized. Use one of: png:cairo png:cg

Also, `dot -v` will identify all available plugins for all the apis.

    	The plugin configuration file:
    	        /home/ellson/FIX/Linux.i686/lib/graphviz/config
    	                was successfully loaded.
     	render      :  canon cmap cmapx dia dot fig gd gd2 gif gtk
    			imap ismap jpeg jpg mif mp pcl pdf pic plain
    			plain-ext png ps ps2 svg svgz vrml vtx wbmp xdot xlib
        	layout      :  circo dot fdp neato twopi
        	textlayout  :  cairo
        	device      :  gtk xlib
        	usershape   :  png-gd

## Installing plugins

In order for dot to know what plugin capabilities are available there is a file called `/usr/lib/graphviz/config` that is created by running `dot -c` at install time. `dot -c` globs the library directory for all filenames that look like they might be plugins, then it goes through and actually loads each of them in turn, positively verifying that they are version compatible and loadable, and it then extracts from each library the set of apis and types that it supports. It saves this information in `/usr/lib/graphviz/config`. When dot is run normally by a user, the single `/usr/lib/graphviz/config` file is loaded but the plugins themselves are not loaded unless a plugin is demanded from that library.

`dot -c` must be run, with enough privileges to write `/usr/lib/graphviz/config`, whenever the set of plugins is changed. For rpm systems `dot -c` is run automatically in %post and %postun scripts in the rpm specfile. If building from sources and installing directly, `dot -c` is run by the `make install` target. If you are cross-compiling and cross-installing then probably the best thing to do is to not use demand loaded plugins at all. There is a `dot_static` binary generated which as a basic set of plugins already builtin.

## Developing new plugins

One of the key goals of the plugin architecture was to allow others to develop new plugins or plugin-libraries. In this section I describe the structure of the key parts of the graphvz-cairo package with a view to reusing or hacking them into a new package.

### A plugin-package

The following files in the source tree for the graphviz-cairo package can probably be used without change:

            graphviz-cairo/
            autogen.sh
            cpl1.0.txt
            mkinstalldirs

The following files should be changed to suit:

            graphviz-cairo/
            AUTHORS
            CHANGELOG.md
            NEWS README
            configure.ac
            Makefile.am
            graphviz-cairo.spec.in

Also in graphviz-cairo are a number of GUI related files specific to graphviz-cairo. These can be probably not useful for any other plugin based on this code:

        graphviz-cairo/
            dot.dot
            dot.png
            dot.xml
            dotedit-splash.dot
            dotedit.desktop.in
            dotedit.indotedit.in
            po/

### A plugin library

Each library in the plugin package has its own subdirectory under plugins:

        graphviz-cairo/
            plugin/
                Makefile.am
                cairo/
                    Makefile.am
                    gvplugin_cairo.c
                gtk/
                    Makefile.am
                    gvplugin_gtk.c
                xlib/
                    Makefile.am
                    gvplugin_xlib.c

graphviz-cairo/plugins/Makefile.am builds all the libraries as SUBDIRS.

In each library subdir Makefile.am and a gvplugin_xxx.c. The gvplugin_cairo.c file is quite short. It defines the primary entry symbol to the library which is a structure defining the name of the library and the address of the table of APIs that it supports.

The name: cairo, the symbol: gvplugin_cairo_LTX_library, and the filename of the library: libgvplugin_cairo.so.2.0.0 must be kept aligned for the plugin mechanism to work.

    #include "gvplugin.h"

    extern gvplugin_installed_t gvrender_cairogen_types;
    extern gvplugin_installed_t gvtextlayout_cairogen_types;

    static gvplugin_api_t apis[] = {
        {API_render, &gvrender_cairogen_types},
        {API_textlayout, &gvtextlayout_cairogen_types},
        {(api_t)0, NULL},
    };

    gvplugin_library_t gvplugin_cairo_LTX_library = { "cairo", apis };

### Plugin APIs

The APIs are defined for the plugins by header files:

        /usr/include/graphviz/gvplugin_device.h
        /usr/include/graphviz/gvplugin_layout.h
        /usr/include/graphviz/gvplugin_render.h
        /usr/include/graphviz/gvplugin_textlayout.h
        /usr/include/graphviz/gvplugin_usershape.h

Also, the set of APIS is defined in graphviz2/lib/gvc/gvplugin.h in: #define APIS ELEM(render) ELEM(layout) ELEM(textlayout) ELEM(device) ELEM(usershape)

In the core of graphviz, each API is wrapped by a set of functions that hide the details of the plugin mechanisms from the caller.

        graphviz2/lib/gvc/gvdevice.c
        graphviz2/lib/gvc/gvlayout.c
        graphviz2/lib/gvc/gvrender.c
        graphviz2/lib/gvc/gvtextlayout.c
        graphviz2/lib/gvc/gvusershape.c

So, to add a new API, minimally what is required is a new header file, extend the #define APIS, and a new file of wrapper functions.

### Plugin types for an API

The cairo library in particular is developing a number of new backends. At some point someone will want to use one or more of these new backends.

(tbd)