13,899,965 members
alternative version

#### Stats

19.6K views
13 bookmarked
Posted 5 Jan 2017
Licenced CPOL

# Using Graphviz in your project to create graphs from dot files

, 5 Jan 2017
Graphviz graphs from dot specification

## Background

The input for Graphviz is a dot file. this simple text file describes the graph layout in a textual manner with it's own grammar: https://en.wikipedia.org/wiki/DOT_(graph_description_language)

## Using the code

Basically there are two ways to incorporate the graphviz functionality:

1. Calling the external program dot.exe supplied by Graphviz and perform data transfer over the file system
2. Wrapping the libraries of Graphviz and perform in memory data transfer

I will show both methods based on Graphviz 2.38.

This file contains the libraries and dot.exe a command line program to generate from a dot graph files an image file.

After download extract the folder: /release/bin. This folder contains dot.exe and related libraries running on windows. This is important because these libraries are build against the right font directories which is of importance when you want to have an aligned and nice looking graph in the end.

Examples of problems that occur when using not the right libraries which can be found in the folder: /release/lib/release/dll. These libraries are build not against the right font folders.

From the /release/bin folder extract the following files:

These are needed to run dot.exe and generate the images from dot graphs.

Testing graph vizualisation

Now we are going to test is everything is working fine

First open a command window in the folder and run from the command line: dot.exe –c

This will cause dot.exe to update the config6 file which contains the administration of available libraries which can be used by GraphViz. If this file is not available of not up to date, then GraphViz (gvc.dll) will malfunction and cause exceptions.

Now we can test dot.exe on the command line by creating a dot file in notepad with the following contents:

digraph g{
A->B
label ="Graph";
labelloc = top;
labeljust = left;
}

And save it as testgraph.dt (use .dt instead of calling it .dot since it will cause windows to recognize it as an office template).

Now run from the command line: dot.exe –Tjpg –O testgraph.dt

When everything works fine a jpg file is created: testgraph.dt.jpg (so .dt is not really needed)

If this works fine than we can proceed.

In both cases we have to add the needed external (Graphviz files) to the visual studio project.

To do this, do the following:

1. Create a map in you visual studio project and call it ‘external’
2. Then via file explorer copy all files mentioned above including dot.exe and config6
3. Next in visual studio go to the map and right click to add existing items
4. Go to the folder external and change the file type from ‘Visual C# files’ to all files (*.*)

Now the files will be shown, select all (including dot.exe and config6) and select add.

The needed external files are now added to your project and we can continue to work out both methods.

Option 1, using the external dot.exe program

Visual studio has added the needed external files to the project, but they won’t be copied during built. Therefore select them all including dot.exe and set the properties to ‘copy always’

Now with each built the support files including dot.exe are copies to the output folder under /external.

(dot.exe can be run from the command line)

Basically we have an input:

String graphVizString = @" digraph g{ label=""Graph""; labelloc=top;labeljust=left;}";

And we want to end with a bitmap. So we have to make a class called ‘FileDotEngine’ which looks as:

public static class FileDotEngine
{
public static Bitmap Run(string dot)
{
string executable = @".\external\dot.exe";
string output = @".\external\tempgraph";
File.WriteAllText(output, dot);

System.Diagnostics.Process process = new System.Diagnostics.Process();

// Stop the process from opening a new window
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;

// Setup executable and parameters
process.StartInfo.FileName = executable;
process.StartInfo.Arguments = string.Format(@"{0} -Tjpg -O", output);

// Go
process.Start();
// and wait dot.exe to complete and exit
process.WaitForExit();
Bitmap bitmap = null; ;
using (Stream bmpStream = System.IO.File.Open(output + ".jpg", System.IO.FileMode.Open))
{
Image image = Image.FromStream(bmpStream);
bitmap = new Bitmap(image);
}
File.Delete(output);
File.Delete(output + ".jpg");
return bitmap;
}
}

On the calling side only the following code is needed to use it as a bitmap:

Bitmap bm = FileDotEngine.Run(graphVizString);

When calling FileDotEngine.Run your application will start an external process (dot.exe) pass the string as input file to it. Then dot.exe will create the image on disk which is read afterwards by your application again and imported as bitmap.

Option 2, using wrapper and call the libraries directly

Visual studio has added the needed external files to the project, but they won’t be copied during built. Therefore select them all except for dot.exe and set the properties to ‘copy always’

Now with each built the support files excluding dot.exe are copies to the output folder under /external.

(dot.exe can't be run from the command line)

Now create a new class and call it: ‘Graphviz’ with the following content:

    public static class Graphviz
{
public const string LIB_GVC = @".\external\gvc.dll";
public const string LIB_GRAPH = @".\external\cgraph.dll";
public const int SUCCESS = 0;

///
/// Creates a new Graphviz context.
///
[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gvContext();

///
/// Releases a context's resources.
///
[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern int gvFreeContext(IntPtr gvc);

///
/// Reads a graph from a string.
///
[DllImport(LIB_GRAPH, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr agmemread(string data);

///
/// Releases the resources used by a graph.
///
[DllImport(LIB_GRAPH, CallingConvention = CallingConvention.Cdecl)]
public static extern void agclose(IntPtr g);

///
/// Applies a layout to a graph using the given engine.
///
[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern int gvLayout(IntPtr gvc, IntPtr g, string engine);

///
/// Releases the resources used by a layout.
///
[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern int gvFreeLayout(IntPtr gvc, IntPtr g);

///
/// Renders a graph to a file.
///
[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern int gvRenderFilename(IntPtr gvc, IntPtr g,
string format, string fileName);

///
/// Renders a graph in memory.
///
[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern int gvRenderData(IntPtr gvc, IntPtr g,
string format, out IntPtr result, out int length);

///
/// Release render resources.
///
[DllImport(LIB_GVC, CallingConvention = CallingConvention.Cdecl)]
public static extern int gvFreeRenderData(IntPtr result);

public static Image RenderImage(string source, string format)
{
// Create a Graphviz context
IntPtr gvc = gvContext();
if (gvc == IntPtr.Zero)
throw new Exception("Failed to create Graphviz context.");

// Load the DOT data into a graph
if (g == IntPtr.Zero)
throw new Exception("Failed to create graph from source. Check for syntax errors.");

// Apply a layout
if (gvLayout(gvc, g, "dot") != SUCCESS)
throw new Exception("Layout failed.");

IntPtr result;
int length;

// Render the graph
if (gvRenderData(gvc, g, format, out result, out length) != SUCCESS)
throw new Exception("Render failed.");

// Create an array to hold the rendered graph
byte[] bytes = new byte[length];

// Copy the image from the IntPtr
Marshal.Copy(result, bytes, 0, length);

// Free up the resources
gvFreeLayout(gvc, g);
agclose(g);
gvFreeContext(gvc);
gvFreeRenderData(result);
using (MemoryStream stream = new MemoryStream(bytes))
{
return Image.FromStream(stream);
}
}
}

This content has been found (so i'm not the author of this piece of code) on the internet and completed/corrected to do what is needed. It is a wrapper around the graphviz libraries (gvc and cgraph) which do the work.

On the calling side only the following code is needed to use it as a bitmap:

Bitmap bm = new Bitmap(Graphviz.RenderImage(graphVizString, "jpg"));

To my opinion the second option is a better solution since the file system is not being used, but debugging is worse since dot.exe (option 1) can give you more insights.

Using this code gives you the following result for an example:

## Points of Interest

The issue with the fonts (misalignment of the text) were a nightmare to find out the issue. Nothing is/has been written on the internet so far.

## History

Keep a running update of any changes or improvements you've made here.

## Share

 Program Manager Netherlands
Engineer with a lot of experience in C-based programming languages and IT in general.

## You may also be interested in...

 Pro Pro

 First Prev Next
 Database Diagrams for SQL Server kiquenet.com20-Dec-18 11:43 kiquenet.com 20-Dec-18 11:43
 Issue on other fonts Hui Yan26-May-18 4:49 Hui Yan 26-May-18 4:49
 images in nodes Mikhail Rusanov3-May-18 0:26 Mikhail Rusanov 3-May-18 0:26
 Paths SteveHolle6-Sep-17 10:09 SteveHolle 6-Sep-17 10:09
 Confused by the examples Zebedee Mason26-Jan-17 2:47 Zebedee Mason 26-Jan-17 2:47
 Re: Confused by the examples Andre van Dun30-Jan-18 3:41 Andre van Dun 30-Jan-18 3:41
 "Create a map and call it ‘external’ Qwertie13-Jan-17 20:46 Qwertie 13-Jan-17 20:46
 Re: "Create a map and call it ‘external’ Andre van Dun30-Jan-18 3:40 Andre van Dun 30-Jan-18 3:40
 My vote of 5 MathiasViolette5-Jan-17 21:13 MathiasViolette 5-Jan-17 21:13
 The images are blurry Bryian Tan5-Jan-17 9:23 Bryian Tan 5-Jan-17 9:23
 Re: The images are blurry Andre van Dun5-Jan-17 9:45 Andre van Dun 5-Jan-17 9:45