Rubjerg.Graphviz 3.0.2

dotnet add package Rubjerg.Graphviz --version 3.0.2
                    
NuGet\Install-Package Rubjerg.Graphviz -Version 3.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rubjerg.Graphviz" Version="3.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Rubjerg.Graphviz" Version="3.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Rubjerg.Graphviz" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Rubjerg.Graphviz --version 3.0.2
                    
#r "nuget: Rubjerg.Graphviz, 3.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=Rubjerg.Graphviz&version=3.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Rubjerg.Graphviz&version=3.0.2
                    
Install as a Cake Tool

Graphviz.NetWrapper

Supported platforms

Rubjerg.Graphviz ships with precompiled Graphviz binaries to ensure the exact graphviz version is used that we have tested and to make deployment more predictable. The current version we ship is Graphviz 11.0.0. This library is compatible with .NET Standard 2.0, but the nuget package only supports .NET5.0 and higher. The unit tests run on windows and linux against .NET Framework .NET 8.0.

Windows

Rubjerg.Graphviz ships with the necessary Graphviz binaries and dependencies built for 64 bit Windows.

Linux

In the same vein as our windows build, we ship Graphviz binaries to make sure that this library is deployed with the same binaries as we've tested it. However, we do not ship all the dependencies of Graphviz. You will have to make sure these are available on your system, if you need them. Here is a list of all the graphviz dependencies. In practice you may not need all of those. In particular, if you only want to read graphs and run the DOT layout algorithm, libc and libz are enough. To run our tests successfully you will also need libgts and libpcre2 (for the neato algorithm). For more details, check the dependencies of any graphviz binaries with ldd.

It is currently not possible to use this package on linux in a non-portable application, i.e. an application that targets linux-x64. The reason for this is that dotnet flattens the directory structure of native dependencies when targetting a single runtime, and this breaks graphviz.

Installation

Add the Rubjerg.Graphviz nuget package to your project.

To run the code from this library on windows, you must have the Microsoft Visual C++ Redistributable (2015-2022) installed, which provides the required runtime libraries. You can download it from the official Microsoft website.

Contributing

This project aims to provide a thin .NET shell around the Graphviz C libraries, together with some convenience functionality that helps abstracting away some of the peculiarities of the Graphviz library and make it easier to integrate in an application. Pull request that fall within the scope of this project are welcome.

Documentation

For a reference of attributes to instruct Graphviz have a look at Node, Edge and Graph Attributes. For more information on the inner workings of the graphviz libraries, consult the various documents presented at the Graphviz documentation page.

Tutorial

using NUnit.Framework;
using System.Linq;

namespace Rubjerg.Graphviz.Test;

#nullable enable

[TestFixture()]
public class Tutorial
{
  public const string PointPattern = @"{X=[\d.]+, Y=[\d.]+}";
  public const string RectPattern = @"{X=[\d.]+, Y=[\d.]+, Width=[\d.]+, Height=[\d.]+}";
  public const string SplinePattern =
      @"{X=[\d.]+, Y=[\d.]+}, {X=[\d.]+, Y=[\d.]+}, {X=[\d.]+, Y=[\d.]+}, {X=[\d.]+, Y=[\d.]+}";

  [Test, Order(1)]
  public void GraphConstruction()
  {
    // You can programmatically construct graphs as follows.
    RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Some Unique Identifier");
    // The graph name is optional, and can be omitted. The name is not interpreted by Graphviz,
    // except it is recorded and preserved when the graph is written as a file.

    // The node names are unique identifiers within a graph in Graphviz.
    Node nodeA = root.GetOrAddNode("A");
    Node nodeB = root.GetOrAddNode("B");
    Node nodeC = root.GetOrAddNode("C");

    // The edge name is only unique between two nodes.
    Edge edgeAB = root.GetOrAddEdge(nodeA, nodeB, "Some edge name");
    Edge edgeBC = root.GetOrAddEdge(nodeB, nodeC, "Some edge name");
    Edge anotherEdgeBC = root.GetOrAddEdge(nodeB, nodeC, "Another edge name");

    // An edge name is optional and omitting it will result in a new nameless edge.
    // There can be multiple nameless edges between any two nodes.
    Edge edgeAB1 = root.GetOrAddEdge(nodeA, nodeB);
    Edge edgeAB2 = root.GetOrAddEdge(nodeA, nodeB);
    Assert.AreNotEqual(edgeAB1, edgeAB2);

    // We can attach attributes to nodes, edges and graphs to store information and instruct
    // Graphviz by specifying layout parameters. At the moment we only support string
    // attributes. Cgraph assumes that all objects of a given kind (graphs/subgraphs, nodes, or
    // edges) have the same attributes. An attribute has to be introduced with a default value
    // first for a certain kind, before we can use it.
    Node.IntroduceAttribute(root, "my attribute", "defaultvalue");
    nodeA.SetAttribute("my attribute", "othervalue");

    // Attributes are introduced per kind (Node, Edge, Graph) per root graph.
    // So to be able to use "my attribute" on edges, we first have to introduce it as well.
    Edge.IntroduceAttribute(root, "my attribute", "defaultvalue");
    edgeAB.SetAttribute("my attribute", "othervalue");

    // To introduce and set an attribute at the same time, there are convenience wrappers.
    edgeBC.SafeSetAttribute("arrowsize", "2.0", "1.0");
    // If we set an unintroduced attribute, the attribute will be introduced with an empty
    // default value.
    edgeBC.SetAttribute("new attr", "value");

    // Some attributes - like "label" - accept HTML strings as value.
    // To tell Graphviz that a string should be interpreted as HTML use the designated methods.
    Node.IntroduceAttribute(root, "label", "defaultlabel");
    nodeB.SetAttributeHtml("label", "<b>Some HTML string</b>");

    // We can simply export this graph to a text file in dot format.
    root.ToDotFile(TestContext.CurrentContext.TestDirectory + "/out.dot");

    // A word of advice, Graphviz doesn't play very well with empty strings.
    // Try to avoid them when possible. (https://gitlab.com/graphviz/graphviz/-/issues/1887)
  }

  [Test, Order(2)]
  public void Layouting()
  {
    // If we have a given dot file (in this case the one we generated above), we can also read
    // it back in.
    RootGraph root = RootGraph.FromDotFile(TestContext.CurrentContext.TestDirectory + "/out.dot");

    // We can ask Graphviz to compute a layout and render it to svg.
    root.ToSvgFile(TestContext.CurrentContext.TestDirectory + "/dot.svg");

    // We can use layout engines other than dot by explicitly passing the engine we want.
    root.ToSvgFile(TestContext.CurrentContext.TestDirectory + "/neato.svg", LayoutEngines.Neato);

    // Or we can ask Graphviz to compute the layout and programatically read out the layout
    // attributes This will create a copy of our original graph with layout information attached
    // to it in the form of attributes. Graphviz outputs coordinates in a bottom-left originated
    // coordinate system. But since many applications require rendering in a top-left originated
    // coordinate system, we provide a way to translate the coordinates.
    RootGraph layout = root.CreateLayout(coordinateSystem: CoordinateSystem.TopLeft);

    // There are convenience methods available that parse these attributes for us and give
    // back the layout information in an accessible form.
    Node nodeA = layout.GetNode("A")!;
    PointD position = nodeA.GetPosition();
    Utils.AssertPattern(PointPattern, position.ToString());

    RectangleD nodeboundingbox = nodeA.GetBoundingBox();
    Utils.AssertPattern(RectPattern, nodeboundingbox.ToString());

    // Or splines between nodes.
    Node nodeB = layout.GetNode("B")!;
    Edge edge = layout.GetEdge(nodeA, nodeB, "Some edge name")!;
    PointD[] spline = edge.GetFirstSpline();
    string splineString = string.Join(", ", spline.Select(p => p.ToString()));
    Utils.AssertPattern(SplinePattern, splineString);

    // If we require detailed drawing information for any object, we can retrieve the so called
    // "xdot" operations. See https://graphviz.org/docs/outputs/canon/#xdot for a specification.
    var activeFillColor = System.Drawing.Color.Black;
    foreach (var op in nodeA.GetDrawing())
    {
      if (op is XDotOp.FillColor { Value: Color.Uniform { HtmlColor: var htmlColor } })
      {
        activeFillColor = System.Drawing.ColorTranslator.FromHtml(htmlColor);
      }
      else if (op is XDotOp.FilledEllipse { Value: var boundingBox })
      {
        Utils.AssertPattern(RectPattern, boundingBox.ToString());
      }
      // Handle any xdot operation you require
    }

    foreach (var op in nodeA.GetLabelDrawing())
    {
      if (op is XDotOp.Text { Value: var text })
      {
        Utils.AssertPattern(PointPattern, text.Anchor.ToString());
        var boundingBox = text.TextBoundingBoxEstimate();
        Utils.AssertPattern(RectPattern, boundingBox.ToString());
        Assert.AreEqual(text.Text, "A");
        Assert.AreEqual(text.Font.Name, "Times-Roman");
      }
      // Handle any xdot operation you require
    }

    // These are just simple examples to showcase the structure of xdot operations.
    // In reality the information can be much richer and more complex.
  }

  [Test, Order(3)]
  public void Clusters()
  {
    RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Graph with clusters");
    Node nodeA = root.GetOrAddNode("A");
    Node nodeB = root.GetOrAddNode("B");
    Node nodeC = root.GetOrAddNode("C");
    Node nodeD = root.GetOrAddNode("D");

    // When a subgraph name is prefixed with cluster,
    // the dot layout engine will render it as a box around the containing nodes.
    SubGraph cluster1 = root.GetOrAddSubgraph("cluster_1");
    cluster1.AddExisting(nodeB);
    cluster1.AddExisting(nodeC);
    SubGraph cluster2 = root.GetOrAddSubgraph("cluster_2");
    cluster2.AddExisting(nodeD);

    // COMPOUND EDGES
    // Graphviz does not really support edges from and to clusters. However, by adding an
    // invisible dummynode and setting the ltail or lhead attributes of an edge this behavior
    // can be faked. Graphviz will then draw an edge to the dummy node but clip it at the border
    // of the cluster. We provide convenience methods for this. To enable this feature, Graphviz
    // requires us to set the "compound" attribute to "true".
    Graph.IntroduceAttribute(root, "compound", "true"); // Allow lhead/ltail
    // The boolean indicates whether the dummy node should take up any space. When you pass
    // false and you have a lot of edges, the edges may start to overlap a lot.
    _ = root.GetOrAddEdge(nodeA, cluster1, false, "edge to a cluster");
    _ = root.GetOrAddEdge(cluster1, nodeD, false, "edge from a cluster");
    _ = root.GetOrAddEdge(cluster1, cluster1, false, "edge between clusters");

    var layout = root.CreateLayout();

    SubGraph cluster = layout.GetSubgraph("cluster_1")!;
    RectangleD clusterbox = cluster.GetBoundingBox();
    RectangleD rootgraphbox = layout.GetBoundingBox();
    Utils.AssertPattern(RectPattern, clusterbox.ToString());
    Utils.AssertPattern(RectPattern, rootgraphbox.ToString());
  }

  [Test, Order(4)]
  public void Records()
  {
    RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Graph with records");
    Node nodeA = root.GetOrAddNode("A");
    nodeA.SetAttribute("shape", "record");
    // New line characters are not supported by record labels, and will be ignored by Graphviz.
    nodeA.SetAttribute("label", "1|2|3|{4|5}|6|{7|8|9}");

    var layout = root.CreateLayout();

    // The order of the list matches the order in which the labels occur in the label string
    // above.
    var rects = layout.GetNode("A")!.GetRecordRectangles().ToList();
    var rectLabels = layout.GetNode("A")!.GetRecordRectangleLabels().Select(l => l.Text).ToList();
    Assert.AreEqual(9, rects.Count);
    Assert.AreEqual(new[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, rectLabels);
  }

  [Test, Order(5)]
  public void StringEscaping()
  {
    RootGraph root = RootGraph.CreateNew(GraphType.Directed, "Graph with escaped strings");
    Node.IntroduceAttribute(root, "label", "\\N");
    Node nodeA = root.GetOrAddNode("A");

    // Several characters and character sequences can have special meanings in labels, like \N.
    // When you want to have a literal string in a label, we provide a convenience function for
    // you to do just that.
    nodeA.SetAttribute("label", CGraphThing.EscapeLabel("Some string literal \\N \\n |}>"));

    // When defining portnames, some characters, like ':' and '|', are not allowed and they
    // can't be escaped either. This can be troubling if you have an externally defined ID for
    // such a port. We provide a function that maps strings to valid portnames.
    var somePortId = "port id with :| special characters";
    var validPortName = Edge.ConvertUidToPortName(somePortId);
    Node nodeB = root.GetOrAddNode("B");
    nodeB.SetAttribute("shape", "record");
    nodeB.SetAttribute("label", $"<{validPortName}>1|2");

    // The conversion function makes sure different strings don't accidentally map onto the same
    // portname.
    Assert.AreNotEqual(Edge.ConvertUidToPortName(":"), Edge.ConvertUidToPortName("|"));
  }
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Rubjerg.Graphviz:

Package Downloads
PowerDocuLib

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.0.2 47 6/30/2025
3.0.1 377 5/28/2025
2.0.2 5,893 2/9/2024
2.0.1 215 1/23/2024
2.0.0 14,807 9/6/2023
1.1.0 4,000 8/8/2022
1.0.8 816 5/27/2021
1.0.7 602 2/22/2021
1.0.6 765 11/18/2020
1.0.5 595 4/17/2020
1.0.4 613 2/13/2020